Contoh penggunaan ImageMagick -- modifikasi animasi
- Anotasi - menambahkan pemberitahuan hak cipta pada SEMUA frame
- Menggambar - memodifikasi animasi hasil coalesce
- Frame demi frame - memodifikasi animasi satu frame pada satu waktu
- Memangkas lebih kecil - membatasi area animasi
- Memangkas kanvasnya juga - viewport crop pada animasi
- Memangkas batas - pengurangan ukuran kanvas otomatis
- Memindahkan posisi frame - menggeser-geser sub-frame
- Membalik animasi - membuat animasi berjalan mundur
- Siklus patroli - bolak-balik antara dua ujung
- Color morphing - perubahan beranimasi antara dua gambar
- Resize morphing - perubahan ukuran beranimasi
- Wipe - membuat wipe dari satu gambar ke gambar lain
- Distorsi beranimasi - distorsi gambar yang beranimasi
- Menyambungkan label - menambahkan label pada animasi
-
Menghapus transparansi (flatten) - menambahkan latar belakang berwarna
-
Menggambar gambar - menimpakan gambar ke animasi
- Komposisi layer - komposisi alpha untuk daftar gambar
- Komposisi gambar tunggal - mengomposisikan layer dengan satu gambar
-
Latar belakang statis - mengomposisikan di atas latar belakang yang lebih besar
-
Animasi glitter - menganimasikan area ber-mask dari sebuah animasi
- Tile glitter - pelapisan bawah 'lubang pada gambar'
- Sparkle - menimpakan glitter transparan
-
Flare dan bintang - menyemai flare dan bintang ke gambar
-
Masalah saat mengubah ukuran animasi (sedang dikerjakan)
- Teknik mengubah ukuran animasi (sedang dikerjakan)
- Resize dengan flatten - solusi umum
- Ledakan tabel warna
- Penyambungan berdampingan (tersinkron waktu)
- Menyambungkan font beranimasi
- Memecah sebuah animasi
- Menggabungkan animasi yang waktunya tidak selaras (sedang dikerjakan)
Halaman ini berisi contoh-contoh praktis tentang bekerja dengan animasi GIF. Sangat disarankan untuk membaca dan memahami dasar-dasar animasi serta setidaknya penanganan umum optimisasi animasi GIF lebih dulu, sebelum mencoba memahami contoh-contoh di sini.
Modifikasi sederhana pada animasi
Pertama, satu hal penting
JANGAN menyimpan animasi antara yang pemrosesannya belum selesai langsung ke GIF, terutama gambar yang belum melalui penanganan semitransparansi atau optimisasi warna dalam bentuk apa pun. Kalau Anda membuat kesalahan besar dengan menyimpannya ke GIF, animasi hasilnya justru menjadi lebih buruk, sebab IM sudah menjalankan kuantisasi warna otomatis untuk mengurangi jumlah warna yang ada agar gambar muat dalam format GIF yang serba terbatas. Bukan hanya itu, IM mengerjakan setiap frame sepenuhnya terpisah dari frame lain, sehingga menghasilkan pilihan warna dan pola dither yang berbeda-beda. Hal itu pada gilirannya membuat pemrosesan berikutnya, terutama optimisasi frame, jauh lebih sulit. Ini sangat penting untuk animasi GIF yang diubah ukurannya, atau yang diberi overlay maupun underlay multi-warna, karena keduanya dapat menambahkan banyak warna ekstra. Format internal IM, MIFF, bisa dipakai sebagai format file sementara bila Anda ingin menggarap animasi secara bertahap, atau gunakan gambar format PNG terpisah untuk setiap frame yang sedang disunting. Pokoknya jangan lakukan penyimpanan akhir ke GIF sebelum yakin tidak akan ada masalah warna. Saya ulangi...
Jangan gunakan GIF sebagai format file antara; gunakan MIFF atau gambar PNG.
Anotasi - menambahkan pemberitahuan hak cipta pada SEMUA frame
Sejak IM versi 6.2.6, sebuah animasi dapat di-"-annotate" begitu saja, dengan cara yang mirip seperti diuraikan di menganotasi di atas gambar. Sebagai contoh, di sini kita memberi anotasi pada animasi dispose previous yang dibuat di dasar-dasar animasi. |
magick canvas_prev.gif -gravity center \
-fill black -annotate +1+1 "Copyright" \
-fill white -annotate +0+0 "Copyright" \
annotate.gif
gif_anim_montage annotate.gif annotate_frames.gif
Alasan cara ini berhasil adalah karena "-annotate" menempatkan teks relatif terhadap kanvas virtual sebuah gambar, bukan relatif terhadap data gambar yang sebenarnya. Dengan begitu posisi teks pada setiap frame sudah benar untuk gambar beranimasi. Namun sebelum versi 6.2.6, "-annotate", sebagaimana banyak operator gambar lain, menempatkan informasi dan overlay relatif terhadap gambar sebenarnya, dan mengabaikan offset page atau kanvas virtual yang mungkin dimiliki sebuah sub-frame. Satu peringatan: menggambar di atas animasi seperti ini tanpa lebih dulu meng-coalesce animasinya dapat menimbulkan efek yang tidak biasa, akibat skema optimisasi yang sudah melekat pada animasi tersebut (lihat kumpulan contoh berikutnya). Karena itu (dan seperti akan terlihat nanti), disarankan lebih dulu menghapus optimisasi frame dan transparansi yang ada dengan meng-coalesce-nya.
Menggambar - memodifikasi animasi hasil coalesce
Nah, meskipun "-annotate" menempatkan teks relatif terhadap kanvas virtual tiap frame, banyak operasi gambar lain tidak begitu. Termasuk di dalamnya semua operasi "-draw", yang hanya menggambar relatif terhadap gambar sebenarnya dan sama sekali mengabaikan offset yang mungkin dimilikinya pada kanvas yang lebih besar. Sebagai contoh, di sini kita menggambar lingkaran hijau yang cantik di dekat sudut kiri atas animasi dispose previous. |
magick canvas_prev.gif -fill LimeGreen -stroke SeaGreen \
-draw 'circle 35,35 20,30' draw_circle_fail.gif
gif_anim_montage draw_circle_fail.gif draw_circle_fail_frames.gif
Nah, seperti terlihat, "-draw" menggambar lingkaran itu relatif terhadap 'gambar sebenarnya', bukan terhadap kanvas virtual (page) yang lebih besar tempat gambar itu berada. Hasilnya, seperti biasa dalam situasi semacam ini... berantakan. Solusi sederhananya adalah lebih dulu meng-coalesce animasi sebelum menggambar, lalu mengoptimisasi ulang animasi GIF-nya setelah itu. Lihat optimisasi animasi untuk detailnya. |
magick canvas_prev.gif -coalesce \
-fill LimeGreen -stroke SeaGreen -draw 'circle 35,35 20,30' \
-layers Optimize draw_circle.gif
gif_anim_montage draw_circle.gif draw_circle_frames.gif
Perhatikan bahwa pengoptimal animasi IM justru memutuskan untuk tidak menimpa bagian yang digambari. Ini sebenarnya lebih optimal daripada bila ia menggambar pada gambar sub-frame itu sendiri. Metode ini memungkinkan Anda menimpakan anotasi, pemberitahuan hak cipta, atau watermark apa pun yang disukai. Tentu saja mungkin diperlukan teknik khusus komposisi layer untuk benar-benar menimpakan sebuah gambar ke setiap frame animasi. Kalau sudah benar-benar mahir, Anda bahkan bisa melangkah sampai penggabungan animasi untuk menimpakan pemberitahuan hak cipta yang beranimasi pada animasi Anda. | _Meskipun teknik 'coalesce-optimize' ini berhasil untuk sebagian besar operasi yang melibatkan animasi, terutama dengan pengoptimal IM, ada beberapa operasi yang mengubah gambar begitu drastis, seperti perubahan warna besar-besaran, shading, dan semitransparansi, sehingga animasi hasilnya gagal dioptimisasi dengan baik.
Sebagai contoh, hampir semua operasi "-resize" cenderung menghasilkan animasi yang optimisasinya menjadi sangat buruk setelah itu, karena perubahan warna yang besar. Lihat mengubah ukuran animasi di bawah untuk solusinya.
Frame demi frame - memodifikasi satu frame pada satu waktu
Dengan memakai operator daftar atau urutan gambar milik IM, tiap frame animasi dapat dimodifikasi secara terpisah. Triknya adalah mengeluarkan tiap frame di dalam tanda kurung, memodifikasinya, lalu mengganti gambar aslinya dengan versi yang sudah dimodifikasi. Sebagai contoh, di sini kita menambahkan teks sebagai watermark hak cipta ke dalam animasi, dalam bentuk animasi juga, sehingga makin sulit dihapus. Supaya animasinya tidak hancur sama sekali, saya juga memakai warna semitransparan. |
magick canvas_prev.gif -coalesce -gravity center \
-font Ravie -pointsize 24 -fill '#FFF8' -stroke '#0008' \
\( -clone 0 -annotate 0 'This' \) -swap 0 +delete \
\( -clone 1 -annotate 0 'This' \) -swap 1 +delete \
\( -clone 2 -annotate 0 'image' \) -swap 2 +delete \
\( -clone 3 -annotate 0 'is' \) -swap 3 +delete \
\( -clone 4 -annotate 0 'Copy\nright' \) -swap 4 +delete \
-layers OptimizeFrame frame_mod.gif
gif_anim_montage frame_mod.gif frame_mod_frames.gif
Perhatikan penggunaan tanda kurung untuk membatasi efek operasi "-annotate" hanya pada 'klona' sederhana dari satu frame animasi. Gambar yang sudah dimodifikasi lalu dikembalikan ke posisinya yang benar dalam urutan gambar memakai operator Swap dan Delete. |
Penggunaan angka tunggal pada operator "-swap" ditambahkan di IM v6.3.4-3. Sebelum itu "-swap 3" harus dituliskan sebagai "-swap -1,3" agar bekerja dengan benar. |
|---|---|
| Teknik memodifikasi frame satu per satu ini mungkin akan menjadi salah satu teknik terpenting yang Anda temui dalam memanipulasi animasi gambar. Anda juga akan melihat bahwa saya sebenarnya menambahkan teks yang sama pada gambar pertama sekaligus gambar kedua. Gambar pertama pada animasi di atas adalah frame antara jeda nol, yang dipakai untuk menetapkan latar belakang bagi sisa animasi ini. Artinya, frame itu berkelebat begitu cepat sehingga biasanya tidak terlihat oleh pengguna, dan memang tidak dimaksudkan untuk terlihat. Jadi dua frame nyata pertama pada animasi di atas sebaiknya dipandang sebagai satu frame yang terlihat, bukan dua frame terpisah. Frame dengan jeda waktu bukan nol adalah frame terakhir dari satu urutan frame 'tampilan'. Begitu pula untuk animasi cepat lainnya, beberapa frame mungkin perlu dimodifikasi agar perubahan Anda terlihat cukup lama. Hal ini bukan masalah untuk anotasi statis yang digambar di atas semua frame (lihat contoh anotasi sebelumnya di atas). Ini membawa kita ke satu hal penting tentang animasi GIF. |
Pelajari sebuah animasi sebelum mencoba memodifikasinya.
Itu bisa membuat perbedaan BESAR pada hasil akhirnya.
Memangkas - membatasi area animasi
IM sudah berupaya membuat operasi gambar "-crop" bekerja dengan benar relatif terhadap kanvas virtual gambar, bukan terhadap gambar sebenarnya (mulai IM versi 6.1.1). Hal ini pada gilirannya memungkinkan hal-hal yang sebelumnya tidak bisa dilakukan secara langsung. Misalnya memangkas gambar-gambar sebuah animasi GIF dan animasinya tetap berjalan sebagaimana mestinya. |
magick canvas_prev.gif -crop 50x50+3+30 cropped.gif
gif_anim_montage cropped.gif cropped_frames.gif
![[IM Output]](../static/img/anim_mods/cropped.gif)
Seperti terlihat, pemangkasannya berhasil, persis seperti pada memangkas satu gambar, dengan mempertahankan offset dan ukuran page yang sesuai, sehingga data gambar tetap terposisi dengan benar meski areanya sudah diperkecil. Dan seperti terlihat, ini tidak mengubah ukuran kanvas virtual secara keseluruhan! | Jangan gunakan "+repage" untuk menghapus offset hasil crop dari animasi GIF yang sudah dioptimisasi frame-nya. Melakukannya juga akan menghapus offset frame yang dibutuhkan untuk menempatkan sub-frame pada kanvas virtual, yang mungkin diandalkan frame-frame berikutnya agar animasi berjalan dengan benar.
Namun operasi "-crop" di atas memang menghasilkan pesan peringatan...
Sebabnya, pemangkasan pada salah satu frame animasi meleset dari gambar overlay sub-frame yang dipakai frame itu; artinya ada satu frame yang tidak memperbarui area yang dipangkas dari animasi. Akibatnya frame tersebut kini tidak berisi gambar nyata sama sekali! Sebagai kompensasi, IM tidak hanya mengeluarkan pesan peringatan, tetapi juga membangkitkan 'Missed Image' khusus sebagai placeholder di dalam animasi agar semuanya tetap teratur, sekaligus mempertahankan metode 'delay' atau 'disposal' yang melekat pada frame itu. Placeholder tersebut boleh dibiarkan, boleh juga diperbaiki sesuka Anda. Dalam kasus ini 'Missed Image' memang dibutuhkan agar animasi berjalan sebagaimana mestinya. Namun bila yang terbangkit adalah beberapa missed image berturut-turut, biasanya semuanya bisa digabung menjadi satu missed image memakai metode "-layers" 'RemoveDups'. Meski begitu, kehati-hatian dan mempelajari animasinya tetap disarankan. (Lihat memecah sebuah animasi di bawah untuk contoh yang lebih rinci.
Memangkas kanvasnya juga - viewport crop pada animasi
Sama seperti crop biasa yang mempertahankan kanvas virtual gambar aslinya, begitu pula crop pada sebuah animasi. Dalam kasus ini itu mungkin bukan yang dimaksudkan. Karena itulah, pada IM versi 6.2.4-5 ditambahkan flag khusus '!' pada argumen "-crop". Flag ini membuat crop tidak hanya memangkas tiap frame gambar, tetapi juga menyesuaikan informasi page atau kanvas gambar ke area yang sama. Ini dikenal sebagai 'viewport crop', karena hasilnya seolah-olah Anda memandang gambar lewat 'jendela' atau 'viewport' seukuran dan seposisi argumen crop. Bukan hanya ukuran kanvas virtual yang disetel ke ukuran area crop, offset tiap frame animasi juga disesuaikan agar semuanya tetap benar. (Lihat viewport crop dengan penyesuaian kanvas/page). Sebagai contoh, mari kita ulangi crop sebelumnya, tetapi kali ini informasi kanvasnya ikut dipangkas memakai flag '!'... |
magick canvas_prev.gif -quiet -crop 50x50+3+30\! crop_viewport.gif
gif_anim_montage crop_viewport.gif crop_viewport_frames.gif
![[IM Output]](../static/img/anim_mods/crop_viewport.gif)
| Karakter '!' punya arti khusus bagi sebagian shell UNIX, seperti "csh", dan harus di-escape dengan backslash, bahkan ketika ditaruh di dalam tanda kutip. IM mengabaikan backslash pada argumen geometry, jadi tidak ada ruginya selalu memberinya backslash.
Seperti terlihat, hasilnya lebih mendekati apa yang sebetulnya ingin Anda capai saat memangkas gambar beranimasi. Perhatikan bahwa saya menyertakan pengaturan (setting) "-quiet" untuk meminta IM tidak memunculkan pesan peringatan tentang Missed Image yang kita bangkitkan pada percobaan crop sebelumnya. Ini disarankan setiap kali memangkas animasi, karena peringatan tersebut memang tidak relevan. Perhatikan pula bahwa viewport crop juga memungkinkan Anda memperbesar area kanvas atau bahkan memindahkan posisi segalanya di dalam kanvas. Namun cara ini berbahaya, sebab gambar apa pun yang jatuh sebagian atau seluruhnya di luar area crop akan terpotong sehingga hanya bagian yang berada di dalam area itu yang tampak. Satu peringatan terakhir. Saat memakai 'viewport crop', gambar-gambar frame bergerak ke arah yang berlawanan dengan offset yang diberikan untuk 'viewport'. Ini bisa terasa tidak logis, kecuali bila Anda ingat bahwa offset pada operator crop adalah posisi viewport, bukan pemindahan langsung gambar-gambarnya sendiri.
Memangkas batas - koreksi ukuran kanvas otomatis
Seperti operasi-operasi sebelumnya, memangkas batas sebuah animasi bisa merepotkan. Bila animasinya berupa animasi frame terhapus yang sederhana, pemangkasan cukup dilakukan dengan menghitung batas maksimum seluruh frame di dalam animasi. Sejak IM v6.3.4-8 hal ini sangat mudah dilakukan memakai metode layer 'TrimBounds'.
magick anim_bgnd.gif -layers TrimBounds anim_trim_bounds.gif
Bagi pengguna versi IM sebelum itu, hal yang sama masih bisa dilakukan, tetapi lewat proses dua langkah (yang juga menjalankan pemrosesan lain yang tidak diinginkan). Caranya, pakai Layers Merge untuk menggabungkan semua frame animasi menjadi satu layer, lalu minta IM melaporkan ukuran dan offset layer tersebut...
magick anim_bgnd.gif -layers merge -format '%wx%h%X%Y' info:
Setelah batas semua frame diketahui, seluruh animasi tinggal di-viewport crop ke ukuran itu.
magick anim_bgnd.gif -crop 89x77+5+10! anim_trim_crop.gif
Bila latar belakang statis juga ingin dipangkas dari animasi, langkah terbaiknya adalah menghapus frame pertama dari animasi yang dioptimisasi frame-nya, sebelum menjalankan langkah Layers Merge. Batas yang dikembalikan lalu dipakai untuk viewport crop pada animasi aslinya.
Memindahkan posisi frame
Operasi serupa yang berkaitan adalah operator 'relative repage'. Operator ini menambahkan offset yang diberikan ke semua layer sub-frame animasi, sehingga posisinya dapat disesuaikan relatif terhadap keseluruhan kanvas. Untuk membuat operasi "-repage" bersifat relatif, tambahkan pula flag '!' pada argumennya. Sebagai contoh, di sini kita menggeser frame kedua dan seterusnya sejauh 30 piksel ke bawah dan ke kanan, lalu mengembalikan frame 'latar belakang' pertama ke posisi normalnya '+0+0. |
magick canvas_prev.gif -repage 0x0+30+30\! \
\( -clone 0 -repage +0+0 \) -swap 0,-1 +delete \
repage_offset.gif
magick identify repage_offset.gif
![[IM Output]](../static/img/anim_mods/repage_offset.gif)
| ![[IM Text]](../static/img/anim_mods/repage_offset.txt.gif)
| Animasi di atas akan gagal (hanya menampilkan dua frame pertama) pada Windows Internet Explorer versi 8. Ini terjadi setiap kali sebuah frame mencoba menggambar gambar melampaui batas kanvas animasi.
Perhatikan bahwa tidak ada satu pun gambar yang di-'crop' atau dipotong. Hanya posisinya yang berubah relatif terhadap gambar latar belakang asli, sekalipun gambarnya bergeser 'ke luar kanvas'. Kalau mau, kanvasnya juga bisa diperluas agar cocok dengan batas baru ini, entah dengan menyesuaikan ukuran kanvas secara langsung... |
magick repage_offset.gif -repage 130x130 repage_canvas.gif
![[IM Output]](../static/img/anim_mods/repage_canvas.gif)
Dengan memakai metode layer trim bounds, batas animasi dapat diperluas secara otomatis, pas cukup untuk mencakup gambar-gambar yang tadi tertempatkan 'di luar batas'... |
magick repage_offset.gif -layers TrimBounds repage_bounds.gif
![[IM Output]](../static/img/anim_mods/repage_bounds.gif)
| _Memakai "-repage" untuk menggeser gambar ke kiri atau ke atas, terutama pada kanvas kecil, kemungkinan besar gagal untuk animasi GIF. Format ini pada dasarnya tidak bisa memakai offset gambar yang negatif.
Untuk itu sebaiknya terapkan pula 'viewport crop', atau pakai 'trim bounds' untuk menggeser semua offset ke dalam kanvas positif yang lebih besar. Kedua cara ini sama-sama menjamin offset positif bagi semua frame gambar.
Format PNG dan MNG sanggup menangani offset negatif, tetapi banyak peramban web dan program lain mungkin tidak memahami offset semacam itu sehingga menghasilkan efek aneh. Satu versi peramban web "Firefox", misalnya, menghasilkan gambar yang teramat besar ketika mencoba menampilkan PNG dengan offset negatif._
Membalik animasi - membuat animasi berjalan mundur, atau berputar
Sejak IM v6.3.3 ditambahkan operator urutan gambar "-reverse" (lihat operator reverse untuk detailnya). Operator ini memungkinkan Anda membalik urutan sebuah animasi hasil coalesce dengan sangat mudah. Sebagai contoh, di sini saya membuat animasi 'k yang ditulis tangan' menjadi terhapus kembali! |
magick script_k.gif -coalesce -reverse \
-quiet -layers OptimizePlus -loop 0 reversed.gif
![[IM Output]](../static/img/anim_mods/reversed.gif)
Saya harus menambahkan lagi opsi "-loop" pada perintah di atas, sebab opsi ini harus melekat pada gambar pertama, yang sekarang justru gambar terakhir! Hasilnya juga masih perlu sedikit penyesuaian waktu, tetapi seperti terlihat, huruf itu kini 'terhapus kembali'! Pastikan urutan gambar di-"-coalesce" dulu sebelum dibalik, karena optimisasi frame yang ada bergantung pada urutan gambar. Lebih baik hapus dulu optimisasi tersebut.
Siklus patroli - bolak-balik antara dua ujung
Teknik serupa adalah menambahkan urutan frame terbalik di akhir animasi, sehingga animasi hasilnya berputar bolak-balik antara frame pertama dan frame terakhir animasi asli. Mirip penjaga yang berpatroli di antara dua titik, dan cara ini disebut 'siklus patroli'. Di sini saya memakai operator duplicate (ditambahkan di IM v6.6.8-7) untuk membangkitkan frame-frame ekstra (yang terbalik). |
magick script_k.gif -coalesce -duplicate 1,-2-1 \
-quiet -layers OptimizePlus -loop 0 patrol_cycle.gif
![[IM Output]](../static/img/anim_mods/patrol_cycle.gif)
Perhatikan bahwa saya tidak menyalin semua gambar animasi, melainkan melewatkan gambar paling pertama dan paling terakhir dari urutan aslinya. Kalau semua gambar disalin, gambar pertama dan terakhir akan tampil dua kali lebih lama dari yang diharapkan, sekaligus membuat file animasinya lebih besar dari yang diperlukan. Meski begitu, waspadai lagi frame antara jeda nol di awal dan akhir animasi, karena frame semacam itu dapat menimbulkan masalah yang tidak terduga. Pada dasarnya jangan lakukan ini tanpa mempelajari animasinya lebih dulu, kalau tidak Anda sedang mengundang masalah. Selain itu, agar hasilnya bisa dioptimisasi lebih baik, Anda bahkan mungkin perlu menambahkan beberapa frame antara jeda nol ekstra di antara siklus maju dan siklus mundur. Frame tambahan seperti itu kemungkinan tidak disediakan pada optimisasi animasi aslinya, sebab keseluruhan animasi biasanya direset saat mengulang. Lihat memecah pembaruan frame untuk penjelasan lebih rinci tentang bagaimana frame tambahan ini membantu optimisasi dan memperbaiki animasi. Berikut metode lama yang memakai operator clone untuk membangkitkan frame duplikat. |
magick script_k.gif -coalesce \( -clone -2-1 \) \
-quiet -layers OptimizePlus -loop 0 patrol_cycle_2.gif
Color morphing - perubahan beranimasi antara dua gambar
Operator "-morph" adalah operator yang benar-benar menarik. Ia mengambil sederet gambar dan menyisipkan frame-frame ekstra di antaranya, sehingga terjadi perubahan warna yang halus dari satu gambar ke gambar berikutnya. Namun operator ini bukan 'morph' sejati, karena ia hanya mengubah warna piksel dan menghasilkan urutan gambar hasil blend. 'Morph' sejati seperti di film juga melibatkan distorsi gambar untuk mengubah garis luar objek pada satu gambar menjadi objek pada gambar yang lain. Sebagai contoh, di sini saya membuat siklus patroli memakai color morph untuk membangkitkan frame ekstra antara gambar rose dan bentuk terbaliknya. |
magick -delay 20 rose: \( +clone -flip \) -morph 5 \
-duplicate 1,-2-1 rose_flip.gif
![[IM Output]](../static/img/anim_mods/rose_flip.gif)
Hasilnya kurang bagus karena semua gambar punya jeda (delay) yang sama. Akibatnya animasi seakan tidak pernah 'beristirahat' atau berhenti sejenak di antara kedua ujung siklus. Solusi yang lebih baik adalah memberi jeda pada gambar asli dan bentuk 'terbalik'-nya. Namun itu menuntut penyesuaian jeda gambar-gambar asli agar berbeda dari gambar-gambar morphing. |
magick rose: \( +clone -flip \) -morph 5 -set delay 10 \
\( -clone 0 -set delay 240 \) -swap 0 +delete \
\( +clone -set delay 240 \) +swap +delete \
-duplicate 1,-2-1 rose_flip_pause.gif
![[IM Output]](../static/img/anim_mods/rose_flip_pause.gif)
Seperti terlihat, jeda waktu bisa menjadi sangat penting untuk menghasilkan animasi yang bagus, karena memungkinkan animasi 'beristirahat' tepat pada titik yang pas. Sejak IM v6.6.9, jeda dapat disetel memakai escape persen FX yang menghitung berdasarkan indeks gambar. Di sini ekspresi FX tersebut berarti: pakai jeda 10 bila indeks gambar bukan yang pertama (t=0) atau yang terakhir (t=n-1), selain itu pakai nilai yang lebih besar. |
magick rose: \( +clone -flip \) -morph 5 \
-set delay '%[fx:(t>0&&t<n-1)?10:240]' \
-duplicate 1,-2-1 rose_flip_anim.gif
![[IM Output]](../static/img/anim_mods/rose_flip_anim.gif)
Untuk aneka metode 'morphing' atau 'transisi' dari satu gambar ke gambar lain, lihat skrip shell ImageMagick "transitions" dan "fxtransitions" karya Fred Weinhaus. Halaman contohnya memuat algoritme dasar yang dipakai skrip tersebut untuk membangkitkan animasinya.
Resize morphing - perubahan ukuran beranimasi
Operator color morph sebenarnya tidak hanya memadukan warna antara dua gambar, tetapi juga mengubah ukuran gambar sekaligus. Sebagai contoh, di sini saya memakai "-morph" pada dua gambar yang berbeda ukuran, bahkan berbeda rasio aspeknya. |
magick rose: medical.gif -morph 10 \
-layers TrimBounds -set dispose previous -coalesce \
-background black -alpha remove \
-set delay '%[fx:(t>0&&t<n-1)?10:60]' \
-duplicate 1,-2-1 -loop 0 morph_resize.gif
![[IM Output]](../static/img/anim_mods/morph_resize.gif)
Hanya baris pertama yang melakukan resize morph. Kalau Anda melihat gambar yang sebenarnya, tiap frame punya ukuran yang berbeda! Dua baris berikutnya 'mengisi penuh' gambar-gambar itu agar berukuran sama, dengan mengisi bagian yang tak terpakai memakai warna hitam. Operasinya sengaja dirancang supaya urutan gambar tidak berpengaruh. Sisanya hanya menyiapkan siklus patroli beserta jeda waktunya. 'Resize' itu hanya dilakukan dari sudut kiri atas. Saat tulisan ini dibuat, operator color morph belum memahami offset layer, ataupun morphing spasial lain (morphing terdistorsi). Karena itu, bila Anda ingin resize-nya terpusat, mungkin diperlukan teknik yang jauh lebih rumit seperti ditunjukkan pada bagian berikutnya. Berikut contoh serupa, kali ini mengubah ukuran gambar dengan versi lebih kecil dari gambar yang sama (rasio aspek dipertahankan)... |
magick rose: \( +clone -resize 10 \) -morph 10 \
-layers TrimBounds -set dispose previous -coalesce \
-background black -alpha remove \
-set delay '%[fx:(t>0&&t<n-1)?10:60]' \
-duplicate 1,-2-1 -loop 0 morph_resize_self.gif
![[IM Output]](../static/img/anim_mods/morph_resize_self.gif)
Perhatikan bahwa gambar-gambar 'antara'-nya lebih buram daripada seharusnya. Ini terjadi karena gambar yang lebih besar bukan hanya diperkecil ukurannya, tetapi juga dipadukan warnanya dengan gambar yang lebih kecil yang tadi diperbesar.
Wipe - membuat wipe dari satu gambar ke gambar lain
Ini sebenarnya sangat mudah dilakukan. Cukup timpakan 'serpihan' tipis dari gambar baru. Serpihan itu dibangkitkan langsung memakai tile crop sederhana. Sebagai contoh, di sini kita mem-'wipe' dari satu gambar ke versi terbaliknya, lalu, sekadar iseng, wipe balik lagi. |
magick rose: \( -clone 0 -flip -crop 3x0 \) \
\( -clone 0 -crop 3x0 \) \
-set delay 10 -loop 0 wipe.gif
![[IM Output]](../static/img/anim_mods/wipe.gif)
Berikut versi dari GeeMack di IM Forums, yang menerapkan wipe dari keempat arah... |
magick -size 100x60 -delay 100 \
gradient:green-yellow gradient:blue-purple \
gradient:orange-white gradient:red-black \
-write mpr:stack -delete 0--1 \
\
mpr:stack'[0]' \( mpr:stack'[1]' -set delay 5 -crop 4x0 \) \
mpr:stack'[1]' \( mpr:stack'[2]' -set delay 5 -crop 0x4 \) \
mpr:stack'[2]' \( mpr:stack'[3]' -set delay 5 -crop 4x0 -reverse \) \
mpr:stack'[3]' \( mpr:stack'[0]' -set delay 5 -crop 0x4 -reverse \) \
-loop 0 wipe_all.gif
Distorsi beranimasi - mendistorsi banyak gambar berdasarkan indeks gambar
Banyak operator dapat memakai escape persen pada argumennya. Artinya, operator itu bisa dimodifikasi supaya bekerja sedikit berbeda untuk setiap gambar yang diproses. Metodenya diawali dengan menggandakan gambar untuk membuat 30 (atau sebanyak yang Anda mau) salinan identik dari gambar rose. Setelah itu tiap gambar dimodifikasi secara berbeda memakai escape persen FX untuk menghitung nilai distorsi, berdasarkan indeks gambar '%[fx:t]' dan jumlah gambar dalam daftar '%[fx:n]'. Sebagai contoh, di sini saya menggeser gambar sejauh nilai yang dihitung. |
magick rose: -duplicate 29 -virtual-pixel tile \
-distort SRT '0,0 1, 0, %[fx:w*t/n],%[fx:h*t/n]' \
-set delay 10 -loop 0 rose_diagonal_roll.gif
![[IM Output]](../static/img/anim_mods/rose_diagonal_roll.gif)
Dan di sini saya memutar gambar bergantung pada indeksnya, tetapi memberi jeda lebih panjang bila indeks gambarnya 0 (gambar pertama). |
magick rose: -duplicate 29 -virtual-pixel Gray \
-distort SRT '%[fx:360*t/n]' \
-set delay '%[fx:t==0?240:10]' -loop 0 rose_rotate.gif
![[IM Output]](../static/img/anim_mods/rose_rotate.gif)
Perhatikan bahwa indeks gambar ('t') bernilai dari '0' sampai 'n-1', sehingga rumus '%[fx:t/n]' bernilai dari '0.0' sampai nilai yang sedikit kurang dari '1.0'. Ini pas untuk animasi berulang atau siklik seperti di atas, tetapi mungkin kurang cocok untuk membangkitkan transisi dari satu gambar ke gambar baru. Untuk kasus itu, bila Anda ingin frame terakhir memiliki pengali '1.0', pakai rumus '%[fx:t/(n-1)]'. Ini baru sekadar contoh dari apa yang kini mudah dilakukan dengan indeks gambar di dalam perhitungan '%[fx:...]'. Bayangkan apa yang mungkin dilakukan dengan distorsi yang lebih rumit. Tanpa perhitungan indeks gambar, hal di atas akan menuntut loop shell dari luar untuk membangkitkan tiap frame satu per satu, plus langkah terpisah untuk mengumpulkan frame menjadi animasi akhir. Contoh skrip shell berloop semacam itu diberikan di animasi gambar melengkung sederhana, sebab operator di sana tidak mengizinkan pemakaian persen escape pada argumennya. | Sebelum IM v6.6.9-0, persen escape dan escape persen FX yang melibatkan indeks gambar, seperti '%p', '%n', '%[fx:t]' dan '%[fx:n]', masih rusak. Biasanya keduanya hanya mengembalikan nilai '0' atau '1' yang tidak berguna, bukan indeks dan jumlah gambar yang sebenarnya dalam urutan gambar saat itu.
Menyambungkan label - menambahkan label pada keseluruhan animasi
Seperti biasa, ada beberapa cara untuk menyambungkan label ke sebuah gambar. Misalnya, untuk animasi yang punya kanvas latar belakang awal, atau yang hanya menimpakan warna baru ke frame sebelumnya, labelnya cukup disambungkan ke frame pertama gambar. Frame-frame lain tidak akan menghapusnya. Di sini kita sekadar menambahkan ruang ekstra dengan "-splice", lalu meng-"-annotate" teks di dalamnya. |
magick canvas_prev.gif \
\( -clone 0 -coalesce -gravity South -background white \
-splice 0x18 -annotate 0 'Label First' \) \
-swap -1,0 +delete label_first.gif
![[IM Output]](../static/img/anim_mods/label_first.gif)
Namun cara ini hanya berhasil untuk sebagian animasi. Ia tidak akan berhasil untuk animasi frame terhapus yang lazim, yang menghapus atau mengganti semua piksel setelah tiap frame ditampilkan. Untuk metode yang lebih umum dan berhasil pada semua animasi, animasinya harus lebih dulu di-"-coalesce" menjadi animasi hasil coalesce yang belum dioptimisasi. Setelah itu labelnya dapat ditambahkan ke setiap frame hasil coalesce, sebelum animasinya dioptimisasi ulang. |
magick canvas_prev.gif -coalesce \
-gravity South -background white -splice 0x18 \
-annotate 0 'A Better Label' \
-layers Optimize labeled_anim.gif
![[IM Output]](../static/img/anim_mods/labeled_anim.gif)
Alih-alih memakai "-annotate" untuk menggambar teks ke dalam ruang ekstra yang ditambahkan, Anda bisa memakai metode komposisi (lihat bagian berikutnya) untuk mengomposisikan sebuah gambar ke ruang tersebut. Dengan begitu label yang jauh lebih menarik dapat disiapkan untuk ditambahkan ke animasi. Tentu saja hal ini bisa membuat sebagian animasi tidak teroptimisasi dengan baik setelahnya, terutama animasi frame terhapus, tetapi itulah harga yang dibayar untuk menambahkan label. Satu solusi untuk animasi jenis itu adalah menyisipkan 'kanvas latar belakang awal' berisi label di depannya, seperti ditunjukkan pada bagian yang menjelaskan animasi frame terhapus. Perhatikan juga bahwa menambahkan label ke animasi bisa membuat banyak warna ekstra ikut bertambah. Ini dapat melampaui batas warna GIF, sehingga Anda mungkin harus siap mengoptimisasi warna animasinya juga. Pekerjaan yang sangat sulit dan sebaiknya dihindari bila memungkinkan (lihat optimisasi warna). Masalah ini bisa muncul pada modifikasi umum apa pun terhadap animasi apa pun.
Menghapus transparansi - menambahkan latar belakang warna polos
Banyak sekali animasi yang Anda temukan di web memiliki latar belakang transparan. Animasi semacam ini sangat berguna karena bisa ditaruh di halaman web tanpa perlu mengkhawatirkan pola latar apa pun yang mungkin ada di sana. Namun ketika animasi diproses, terutama saat operator gambar lain seperti "-resize" dan "-blur" diterapkan, animasi seperti itu bermasalah. Solusi umumnya adalah menghapus transparansi dari gambar, biasanya dengan menimpakannya ke suatu warna tertentu, misalnya warna yang dipakai sebagai latar belakang halaman web.
Sebagai contoh, di sini saya punya animasi overlay transparan sederhana berupa huruf 'K' yang digambar seolah-olah oleh tangan tak terlihat. Karena animasi GIF ini digambar dengan transparansi dan hanya menimpakan gambar ke frame sebelumnya (menambah piksel, tidak pernah menghapusnya), cara sederhana untuk menetapkan warna (atau gambar) latar belakang adalah menambahkannya hanya pada frame pertama animasi. Semua frame lain berisi warna transparan untuk latar belakangnya, jadi tidak akan memengaruhi hasilnya. Di sini kita memakai operator flatten untuk menimpakan frame pertama animasi ke warna latar belakang 'LimeGreen'. Kita boleh memakai "-flatten" untuk ini karena kita hanya menerapkannya pada satu gambar, BUKAN pada keseluruhan animasi. |
magick script_k.gif \( -clone 0 -background LimeGreen -flatten \) \
-swap 0,-1 +delete script_k_flatten_0.gif
![[IM Output]](../static/img/anim_mods/script_k_flatten_0.gif)
Penting juga dicatat bahwa frame pertama aslinya hanya berukuran satu piksel. Operator flatten bukan hanya mewarnai latar belakangnya, tetapi juga memperluas frame itu ke ukuran penuhnya; artinya frame tersebut ikut 'diisi penuh'. Perhatikan pula bahwa hanya frame pertama animasi yang diwarnai. Metode ini lebih disukai karena optimisasi apa pun (seperti optimisasi berat yang dimiliki animasi ini) tetap terjaga. Mewarnai frame pertama tidak akan berhasil untuk semua animasi GIF. Cara itu hanya berhasil untuk animasi overlay sederhana. Untuk metode umum penghapusan transparansi dari sebuah animasi, animasinya harus lebih dulu di-"-coalesce", lalu transparansinya benar-benar dihapus dari semua frame memakai operator alpha remove. Kali ini mari kita lakukan dengan warna latar belakang 'Tomato'. |
magick script_k.gif -coalesce -background Tomato -alpha remove \
-layers Optimize script_k_alpha_rm.gif
![[IM Output]](../static/img/anim_mods/script_k_alpha_rm.gif)
Tentu saja optimisasi hasilnya mungkin tidak seoptimal aslinya, tetapi animasinya tidak lagi mengandung transparansi. Sebagai efek samping tambahan, pengaturan dispose 'Background' apa pun di dalam animasi akan diubah menjadi 'None' atau 'Previous' oleh proses optimisasi frame, sebab transparansi bukan lagi persoalan. | Alpha Remove ditambahkan di IM v6.7.5. Bila versi IM Anda lebih lama, pakai metode alternatif, misalnya memanfaatkan efek samping operator border. Lihat menghapus transparansi untuk detail metode ini dan metode lainnya.
Penanganan latar belakang yang lebih rumit, seperti melapiskan gambar atau pola latar dari bawah, menuntut penanganan animasi yang jauh lebih rumit daripada modifikasi sederhana yang kita lihat sejauh ini. Itulah yang akan kita bahas berikutnya.
Komposisi alpha multi-gambar
Tingkat penanganan animasi berikutnya menuntut kemampuan mengomposisikan satu gambar statis, entah di atas atau di bawah animasi yang sudah ada. Inilah komposisi alpha pada umumnya. Perkaranya menjadi lebih rumit lagi ketika dua kumpulan gambar yang terpisah digabungkan. Sebelum IM v6.3.3-7, komposisi multi-daftar hanya mungkin dilakukan memakai skrip API yang dirancang khusus, atau skrip shell yang menyimpan dan menggabungkan tiap frame animasi satu per satu. Keduanya bukan teknik yang menyenangkan, tetapi hanya itu yang bisa dilakukan. Nah, keadaannya kini sudah berubah.
Menggambar gambar - menggambar sebuah gambar ke atas daftar gambar
Operator "-draw" mampu mengomposisikan gambar sumber di atas sederet gambar. Ia juga satu-satunya metode komposisi alpha multi-gambar yang bisa dipakai dalam perintah "mogrify", atau terhadap banyak gambar, sebelum IM v6.3.3-7. Teknik komposisi alpha ini dulu begitu penting karena memungkinkan Anda menyebut sebuah gambar sebagai argumen yang terpisah dari daftar gambar saat itu, yaitu di dalam bahasa Magick Vector Graphic yang diapit tanda kutip pada "-draw". Karena arti historisnya, saya akan menunjukkan pemakaiannya secara rinci, terutama bagi pengguna yang masih memakai versi IM lama. Sebagai contoh, di sini saya menimpakan gambar rose ke seluruh animasi. |
magick canvas_prev.gif -coalesce \
-gravity NorthEast -draw 'image over 5,5 0,0 "rose:"' \
-layers Optimize draw_over.gif
![[IM Output]](../static/img/anim_mods/draw_over.gif)
Cara ini memungkinkan Anda mengomposisikan gambar sumber dari luar ke atas setiap gambar dalam urutan gambar saat itu. Untuk sebagian besar keperluan, ini sudah cukup. Misalnya, dengan metode komposisi 'Dst_Over' sebuah gambar juga bisa ditaruh 'di bawah' animasi sebagai latar belakang statis. Sebagai contoh, di sini kita melapiskan gambar bawaan "netscape:" dari bawah, padahal file gambar dari luar mana pun sebenarnya bisa dipakai... |
magick script_k.gif -coalesce \
-draw 'image DstOver 0,0 0,0 "netscape:"' \
-layers Optimize script_k_netscape.gif
![[IM Output]](../static/img/anim_mods/script_k_netscape.gif)
Perhatikan bahwa ukuran animasinya tidak berubah, sebab gambar tujuan-lah yang menentukan ukuran akhir komposisi alpha. Bila Anda memang ingin membuat kanvas yang lebih besar, ukuran dan offset animasinya harus disesuaikan agar muat menampung latar belakangnya. Misalnya dengan relative repage pada animasi sebelum di-coalesce. |
magick script_k.gif -repage 100x100+20+20\! -coalesce \
-draw 'image DstOver 0,0 0,0 "granite:"' \
-layers Optimize script_k_granite.gif
![[IM Output]](../static/img/anim_mods/script_k_granite.gif)
Selain itu, bila Anda ingin memakai gambar yang sudah dibaca, dibuat, atau dimodifikasi, gunakan "MPR: Memory Program Register sebagai 'sumber baca' untuk gambar tersebut. |
magick -size 53x54 xc:SkyBlue -fill DodgerBlue \
-draw 'circle 26,27 24,8' -write mpr:bgnd +delete \
\
script_k.gif -coalesce \
-draw 'image DstOver 0,0 0,0 "mpr:bgnd"' \
-layers Optimize script_k_mpr_bg.gif
![[IM Output]](../static/img/anim_mods/script_k_mpr_bg.gif)
Kira-kira sampai di situlah batas metode komposisi alpha dengan draw. Tidak ada cara menimpakan gambar-gambar animasi 'ke atas' gambar tujuan yang ukurannya tidak diketahui, dan tidak ada cara menggabungkan dua urutan multi-gambar yang terpisah. Setidaknya sampai...
Komposisi layer - komposisi alpha untuk daftar gambar
Pada IM v6.3.3-7 ditambahkan metode "-layers" '**Composite**', yang memungkinkan Anda mengomposisikan dua kumpulan gambar yang sepenuhnya terpisah. (Untuk ringkasan singkat lihat pelapisan gambar, layer composite) Untuk melakukannya di baris perintah, dibutuhkan gambar penanda 'null:' khusus yang menandai di mana daftar gambar tujuan pertama berakhir dan daftar gambar sumber yang ditimpakan dimulai. Hanya itu satu-satunya kerumitan nyata dari metode ini. Jadi mari kita coba dengan membuat sekumpulan bayangan dari sekumpulan gambar, lalu menimpakan gambar aslinya di atas gambar-gambar bayangan tersebut... |
magick script_k.gif -coalesce coalesced_k.gif
magick coalesced_k.gif -background black -shadow 100x3+2+5 \
-background SkyBlue -alpha remove shadows_k.gif
magick shadows_k.gif null: coalesced_k.gif \
-layers Composite compose_shadow.gif
gif_anim_montage compose_shadow.gif compose_shadow_frames.gif
Contoh di atas sangat penting, jadi akan saya jelaskan secara rinci. Pertama kita membangkitkan versi hasil coalesce dari animasi kita untuk menghapus optimisasi apa pun yang mungkin ada, sehingga animasinya siap untuk pemrosesan serius tanpa diganggu optimisasi GIF. Berikutnya kita membuat gambar bayangan beranimasi dari animasi hasil coalesce tadi, lalu menghapus transparansinya, sebab GIF tidak sanggup menangani bayangan semitransparan. Inilah animasi yang ingin kita tambahkan 'di bawah' animasi asli kita. Jumlah frame-nya sama, bahkan pengaturan waktunya pun sama dengan animasi aslinya. Kesesuaian ini penting, jadi jangan sampai terlupa. Sekarang kita membaca kedua urutan animasi atau layer tersebut, tetapi kita menyisipkan pemisah gambar 'null:' khusus di antara keduanya, supaya ImageMagick tahu kapan satu urutan berakhir dan urutan berikutnya dimulai. Pemisah gambar ini otomatis dibuang oleh operasi "-layer composite" yang teramat penting itu. API lain semestinya bisa memakai 'Wand' gambar yang terpisah, alih-alih satu urutan dengan pemisah khusus. Komposisi layer kemudian dijalankan persis seolah-olah kedua animasi atau urutan gambar ini hanyalah satu gambar tunggal biasa, bukan urutan berisi banyak gambar. Setiap pasang gambar, satu tujuan dan satu sumber, dikomposisikan bersama sehingga menghasilkan urutan gambar yang tergabung (terkomposisi). Hasil akhirnya, kita sudah menambahkan bayangan pada urutan animasi asli kita, yang kini siap dioptimisasi GIF atau langsung dipakai.
Semua langkah di atas sebenarnya bisa dikerjakan dalam satu perintah. Namun Anda tidak bisa begitu saja memakai "-clone" untuk membuat salinan urutan aslinya, sebab kita tidak benar-benar tahu (dan tidak ingin tahu) ada berapa gambar dalam urutan itu. Sebagai gantinya, pakai "MPR: Memory Program Register untuk menyimpan satu daftar gambar utuh. Kira-kira seperti memotret seluruh urutan gambar yang sedang ada di memori, lalu membacanya kembali nanti. Hasilnya berupa perintah seperti ini, meski saya memakai warna latar belakang yang berbeda. |
magick script_k.gif -coalesce -write mpr:images \
-background black -shadow 100x3+2+5 \
-bordercolor Wheat -border 0 \
null: mpr:images -layers Composite \
magick composite_shadow.gif
![[IM Output]](../static/img/anim_mods/composite_shadow.gif)
Versi ini sebenarnya bekerja lebih baik karena kita tidak kehilangan informasi offset yang dihasilkan operator shadow (GIF tidak bisa menyimpan offset negatif, jadi ia mereset offset itu menjadi nol). Hal tersebut bisa diperbaiki pada contoh di atas dengan memakai format file MIFF untuk gambar-gambar antara alih-alih GIF, atau, seperti akan terlihat pada contoh berikutnya, dengan memakai offset komposisi "-geometry". Pada dasarnya contoh-contoh ini menunjukkan bahwa operator layers composite memang memahami pengaturan offset kanvas virtual ("-page") tiap gambar dan akan menanganinya, persis seperti operator layers flatten, atau lebih baik lagi layers merge, menanganinya. Namun operator layers composite juga memahami penggunaan offset composite geometry ("-geometry", bawaannya nol) untuk mengendalikan penempatan keseluruhan urutan gambar yang ditimpakan. Ia bahkan memahami efek "-gravity" pada offset global tersebut. Sebagai contoh.. mari kita timpakan animasi 'K' asli kita di sisi 'South' dari animasi bayangan yang dibangkitkan tadi... |
magick shadows_k.gif null: coalesced_k.gif \
-geometry +0-10 -gravity South -layers Composite \
magick composite_south.gif
![[IM Output]](../static/img/anim_mods/composite_south.gif)
Contoh di atas juga menunjukkan bahwa, persis seperti komposisi alpha dua gambar biasa, urutan gambar tujuanlah yang menentukan ukuran gambar keluaran akhir, dan overlay komposisi apa pun akan dipotong mengikuti gambar kanvas tujuan. Karena itu pastikan semua gambar tujuan cukup besar untuk memuat hasil akhir Anda. | Kemampuan resize milik "-geometry" sebenarnya bukan bagian dari operasi composite; ia hanya mengubah ukuran gambar terakhir dari urutan gambar saat itu. Karena itu hasilnya tidak akan seperti yang Anda harapkan bila dipakai bersama komposisi layer multi-gambar. Lihat resize geometry untuk detailnya.
Pada dasarnya komposisi layer sangat mirip dengan komposisi biasa.
Sederhana sekali, sebenarnya. Tadi saya bilang sederhana?
Detail komposisi layer......Seperti terlihat di atas, versi baris perintah dari "-layers Composite" memakai gambar 'null:' pertama yang ditemukan dalam daftar gambar saat itu sebagai penanda pemisah kedua daftar. Kedua daftar gambar dipisahkan dan 'null:'-nya dibuang sebelum keduanya dikomposisikan alpha, dua gambar sekaligus. Hanya gambar yang dibangkitkan dari sumber gambar 'null:' khusus yang bisa dipakai sebagai penanda, dan bila tidak ditemukan akan dilaporkan error. Saat ini gambar penanda 'null:' ini tidak bisa dibaca dari pipeline (setidaknya belum), hanya bisa dibangkitkan saat diperlukan. Komposisi layer juga lebih rumit daripada komposisi alpha dua gambar yang sederhana, sebab kanvas virtual gambar-gambar dalam daftar itu ikut diperhitungkan. Biasanya komposisi alpha mengabaikan ukuran dan offset kanvas virtual untuk keperluan penempatan, dan hanya memakai ukuran gambar yang sebenarnya. Metode layers khusus ini memakai informasi kanvas virtual untuk penempatan geometry, supaya kedua urutan gambar bisa disejajarkan. Untuk itu, offset kanvas virtual apa pun yang dimiliki sebuah sub-frame ikut ditambahkan ke offset komposisi "-geometry" yang sudah disesuaikan "-gravity", untuk menghitung posisi overlay gambarnya. Hanya ukuran kanvas virtual "-page" milik gambar pertama tiap daftar yang dipakai untuk menghitung penyesuaian "-gravity" terhadap offset komposisi "-geometry". Ukuran kanvas gambar-gambar berikutnya diabaikan, dan hanya offset virtual "-page" masing-masing yang ditambahkan ke offset "-geometry" hasil hitungan. Dengan kata lain "-layers Composite" dirancang untuk komposisi alpha 'layer' atau 'animasi', beserta kebutuhan khusus daftar gambar semacam itu. Hal yang perlu diwaspadai...Meski begitu Anda tetap harus berhati-hati dengan daftar gambar yang ditimpakan. Bila misalnya gambar-gambar daftar tujuan kurang besar, atau posisinya tidak tepat untuk memuat gambar sumber yang ditimpakan, gambar yang ditimpakan akan terpotong, atau meleset sama sekali dari gambar tujuan. Karena itu ada baiknya gambar tujuan di-coalesce dulu ke ukuran kanvas penuh sebelum gambar sumber yang lebih kecil ditimpakan. Lihat misalnya contoh penyambungan animasi berdampingan di bawah, yang ukuran kanvasnya perlu diperluas untuk menyediakan ruang bagi gambar yang disambungkan. Selain itu, bila daftar gambar sumber berupa animasi GIF, pastikan sub-frame-nya bersih dari hal-hal seperti optimisasi kompresi dan optimisasi frame yang rumit; kalau tidak, Anda bisa menemui masalah. Sebaliknya, animasi frame terhapus atau animasi hasil coalesce dapat langsung di-'Composite' tanpa masalah apa pun. Ingat saja bahwa komposisi layer tidak memahami metode disposal GIF apa pun yang mungkin melekat pada gambar, meskipun ia mempertahankan meta-data animasi GIF tujuan, seperti metode dispose, jeda frame, dan batas iterasi loop. Satu pengecualian untuk hal ini diberikan pada kasus khusus di bawah.
Komposisi gambar tunggal - mengomposisikan gambar dengan satu gambar
Biasanya dua daftar gambar yang panjangnya sama dikomposisikan bersama, sepasang gambar pada satu waktu sampai salah satu daftar gambar habis. Tidak ada daftar gambar yang diulang. Komposisinya akan berhenti begitu saja. Yang tersisa hanyalah daftar gambar tujuan asli beserta hasil komposisi yang ditambahkan. Gambar pemisah 'null:' dan semua gambar sumber dihapus dari daftar gambar saat itu. |
Antarmuka API untuk metode layers ini semestinya membiarkan Anda menghasilkan dua daftar gambar terpisah, dan terserah Anda untuk menghapus kedua daftar gambar masukan yang dipakai untuk menghasilkan daftar gambar akhirnya. Pemisah 'null:' semestinya tidak diperlukan. |
|---|---|
| Namun bila salah satu daftar hanya berisi satu gambar, gambar itu akan dikomposisikan terhadap semua gambar pada daftar yang lain. Tidak jadi soal apakah gambar tunggal tersebut gambar sumber atau gambar tujuan. Metode ini akan mengomposisikannya terhadap daftar gambar yang lain, dan mempertahankan meta-data GIF milik daftar gambar , bukan milik gambar tunggalnya, sekalipun gambar itu adalah gambar tujuan. 'Komposisi terhadap satu gambar' ini adalah kasus khusus komposisi layer, dan sangat berguna untuk menambahkan latar belakang pada animasi (lihat berikutnya), atau menyisipkan objek statis ke dalam animasi. |
Latar belakang statis - mengomposisikan di atas latar belakang yang lebih besar
Sebagai contoh, dengan metode khusus komposisi layer gambar tunggal ini kita dapat mengomposisikan sebuah animasi di atas latar belakang statis... |
magick -size 100x100 plasma:fractal null: \( script_k.gif -coalesce \) \
-gravity Center -layers Composite \
-layers Optimize magick composite_background.gif
![[IM Output]](../static/img/anim_mods/composite_background.gif)
Karena gambar latar belakang berperan sebagai tujuan , ia menentukan ukuran akhir animasi, tetapi seluruh meta-data (jeda, label, komentar, dan sebagainya) diambil dari daftar gambar sumber. Biasanya informasi itu berasal dari daftar gambar tujuan. Inilah satu-satunya saat gambar sumber menyediakan informasi meta-data selama komposisi gambar. Perhatikan juga bahwa karena komposisi layer memahami "-gravity", gambarnya terpusat dengan benar tanpa Anda perlu menghitung sendiri. Namun bila frame sumbernya mengandung offset, offset itu akan ditambahkan pula ke posisi yang ditentukan gravity, sehingga posisi relatif semua sub-frame tetap benar. Perhatikan bahwa karena animasi "script_k.gif" sebenarnya sejenis animasi overlay, ada metode alternatif untuk menambahkan latar belakang statis padanya. Lihat bagian menghapus transparansi di atas untuk contohnya (ke warna polos, tetapi bisa gambar apa pun). Hal yang sama berlaku untuk animasi frame terhapus yang bahkan lebih sederhana. Dalam kasus itu animasinya bahkan tidak perlu di-coalesce dulu, melainkan bisa langsung dikomposisikan ke gambar latar belakang. Namun setelahnya Anda mungkin perlu men-"-set" metode 'dispose' yang dipakai, atau lebih baik lagi meng-Optimize animasi yang sepenuhnya hasil coalesce. Sementara itu, jenis animasi teroptimisasi lainnya menuntut operasi "-coalesce" tersebut, plus komposisi penuh dengan semua frame animasi. Karena itu mungkin lebih baik memakai metode di atas, sekadar untuk memastikan semua animasi GIF ditangani dengan benar.
Segala yang berkilauan...
Animasi glitter
Metode komposisi layer di atas membuat pembuatan animasi sederhana, seperti glitter, jauh lebih mudah. Pertama kita butuh glitter yang cukup besar untuk menutupi gambar yang sedang diproses. Di sini saya akan membangkitkan animasi glitter tiga gambar dari beberapa gambar bintik acak. Yang pertama adalah glitter hitam-putih mentah di atas transparansi murni, yang membangkitkan 3 frame glitter dengan memisahkan ketiga kanal warna menjadi gambar kanal hitam-putih. Ini pada dasarnya titik awal mentah untuk membangkitkan jenis glitter yang lain. Ambang batas (threshold) '30%' mengatur berapa banyak 'titik' pada tiap frame. |
magick -size 100x100 xc: +noise Random -separate \
null: \( xc: +noise Random -separate -threshold 30% -negate \) \
-compose CopyOpacity -layers composite \
-set dispose background -set delay 20 -loop 0 glitter_overlay.gif
![[IM Output]](../static/img/anim_mods/glitter_overlay.gif)
Dari glitter 'mentah' ini, Anda bisa menimpakannya memakai komposisi alpha 'Screen' agar hanya mencerahkan sebagian warna, sehingga terbangkit glitter dengan warna tertentu. Saya memakai metode border flatten (di atas). Cukup satu warna polos... |
magick glitter_overlay.gif \
-compose Screen -bordercolor GoldenRod -border 0x0 glitter_gold.gif
![[IM Output]](../static/img/anim_mods/glitter_gold.gif)
Dengan komposisi layer, Anda juga bisa memakai satu gambar, atau bahkan beberapa gambar, untuk menyediakan latar belakang berwarna yang beragam. Sebagai contoh, di sini saya membangkitkan tiga gambar plasma fraktal untuk memberi pewarnaan yang sedikit acak pada pola glitter-nya. |
magick glitter_overlay.gif null: -size 100x100 \
plasma:red-firebrick plasma:red-firebrick plasma:red-firebrick \
-compose Screen -layers composite glitter_plasma.gif
![[IM Output]](../static/img/anim_mods/glitter_plasma.gif)
Tentu saja masih ada banyak gaya glitter dan pola gerakan lain. Banyak tile glitter semacam itu bisa dicari dan diunduh dari WWW. Untuk menerapkan glitter seperti ini pada sebuah gambar, ada beberapa metode yang berbeda. Biasanya glitter-nya di-mask ke bentuk dan/atau latar belakang tertentu. Untuk itu bisa dipakai bentuk transparan (dikomposisikan memakai DstIn)
magick -size 100x100 -fill white -background none -font Candice \
-gravity center -pointsize 90 label:A glitter_mask_trans.gif
magick glitter_plasma.gif null: glitter_mask_trans.gif -alpha set \
-compose DstIn -layers composite glitter_masked_trans.gif
Atau gambar mask hitam-putih (dikomposisikan memakai CopyOpacity)
magick -size 100x100 -fill white -background black -font Candice \
-gravity center -pointsize 90 label:A glitter_mask.gif
magick glitter_plasma.gif null: glitter_mask.gif -alpha off \
-compose CopyOpacity -layers composite glitter_masked.gif
Baik, kita sudah punya area yang di-mask. Gambarnya bisa diselesaikan, umumnya dengan menimpakan glitter yang sudah di-mask ke gambar aslinya. Namun dalam kasus kita, saya akan melapiskan latar belakang dari bawah dan menimpakan border di atasnya. |
magick glitter_masked.gif -size 100x100 \
null: gradient:gold1-gold4 -compose DstOver -layers composite \
null: \( -fill none -background none -stroke white -strokewidth 2 \
-font Candice -gravity center -pointsize 90 label:A \) \
-compose over -layers composite glittered_letter.gif
![[IM Output]](../static/img/anim_mods/glittered_letter.gif)
Contoh terakhir ini juga membereskan masalah transparansi GIF, dengan menghapus seluruh transparansi dari gambar akhir dan menimpakan border yang halus di sekeliling area berglitter. | Meskipun di atas saya memakai gambar format GIF supaya bisa menampilkan tiap langkah prosesnya, dalam praktiknya Anda sebaiknya menggabungkan semua langkah menjadi satu perintah, atau memakai format file gambar antara yang lebih baik seperti MIFF. Itu dilakukan untuk menghindari masalah bawaan format GIF sampai pekerjaannya selesai.
Tile glitter - pelapisan bawah 'lubang pada gambar'
Seperti disebut tadi, ada banyak gambar tile glitter beranimasi yang sudah jadi dan tersedia di WWW (coba cari "glitter tiles"). Salah satu sumbernya adalah pengguna IM Studio bernama scri8e dan situsnya, Moons Stars. Tapi awas, menurut saya kebanyakan tile glitter tampak agak mengerikan, atau terlalu cepat.
Untuk contoh ini saya mencari lalu memodifikasi satu tile glitter biru berisi beberapa pola bintang kecil. Saya pikir tile itu berguna untuk memberi pakaian berkilau pada sang penyihir IM, supaya ia tampak benar-benar ajaib. Cara termudah memberi glitter pada gambar yang sudah ada mungkin adalah melubangi gambarnya, alih-alih berusaha mem-mask pola glitter-nya. Namun cara ini hanya berhasil untuk gambar yang sejak awal tidak mengandung transparansi. Sebagai alternatif, transparansi gambar bisa dihapus lebih dulu, lalu ditambahkan kembali setelah selesai. Jadi mari kita ambil logo IM Examples, dan pakai penggantian warna untuk memotong semua bagian biru dari gambar itu. Semacam memberi jubah menghilang kepada penyihir kita ;-)
magick logo.gif -alpha set -fuzz 33% -transparent blue logo_holed.gif
Perhatikan penggunaan faktor fuzz untuk mengatur seberapa banyak warna biru yang harus dihapus. Tapi awas, ini bukan cara yang bagus untuk memotong sebagian area gambar, sebab hasilnya berupa tepi ber-alias. Sayangnya belum ada fitur pemotongan berpinggiran halus yang sederhana sampai saat ini. Baiklah, kita sudah punya gambar berlubang (atau banyak lubang). Langkah berikutnya adalah melapiskan gambar tile glitter dari bawah. Masalahnya, tile di atas terlalu kecil sehingga tidak akan menutupi seluruh gambar! Berikut ini memakai teknik yang agak rumit untuk men-tile tile glitter multi-gambar tersebut. Meski begitu Anda tetap harus memberi ukuran yang lebih besar dari gambar aslinya supaya gambar itu benar-benar tertutup. |
magick glitter_blue.gif -virtual-pixel tile \
-set option:distort:viewport 180x180 -distort SRT 0 \
glitter_blue_tiled.gif
![[IM Output]](../static/img/anim_mods/glitter_blue_tiled.gif)
Sekarang mari kita pakaikan busana baru pada sang penyihir, dengan menaruh glitter yang di-tile di atas tadi di bawah gambar yang 'bolong-bolong'. |
magick logo_holed.gif null: glitter_blue_tiled.gif \
-compose DstOver -layers composite \
-loop 0 -layers Optimize logo_glittered.gif
![[IM Output]](../static/img/anim_mods/logo_glittered.gif)
Tentu saja semua langkah ini bisa dikerjakan dalam satu perintah. Di sini saya membatasi pembuatan lubang hanya pada jubah sang penyihir, yang punya dua bagian tertentu yang terpisah. |
magick logo.gif -alpha set -fuzz 10% -fill none \
-draw 'matte 120,150 floodfill matte 150,120 floodfill' \
null: \( glitter_blue.gif -virtual-pixel tile \
-set option:distort:viewport 300x300 -distort SRT 0 \) \
-compose DstOver -layers composite \
-loop 0 -layers Optimize logo_glitter_cloak.gif
![[IM Output]](../static/img/anim_mods/logo_glitter_cloak.gif)
Lubang-lubang di atas dibuat memakai primitif draw matte fill untuk memilih titik dan warna nyata dari gambar sebagai acuan penggantian warna. Artinya saya tidak perlu memakai faktor fuzz setinggi yang saya pakai semula, sebab warna pembandingnya diambil dari area tertentu yang dipilih. Saya juga memakai 'viewport' tiling yang lebih besar untuk memastikan gambar yang di-tile tertutup seluruhnya, tanpa perlu tahu dimensi persisnya. | _Penggunaan operator distorsi umum beserta opsi khusus "viewport' miliknya (ditambahkan di IM 6.3.6-0) juga memberi Anda kesempatan memodifikasi pola distorsinya dengan cara khusus lain. Misalnya memberinya tampilan 'perspektif' atau memutar polanya ke sudut yang tidak siku-siku. Melakukan hal itu dapat memperkaya tiling-nya sehingga tidak tampak terlalu seragam.
Untuk beberapa contoh lihat Distort Affine Tiling._
Sparkle - menimpakan glitter yang sebagian besar transparan
Masalah utama kedua teknik animasi glitter sebelumnya adalah sifat penggantiannya yang serba-atau-tidak sama sekali. Shading atau latar belakang asli gambar tidak bisa dipakai. Selain itu glitter-nya sepenuhnya terkurung di area yang di-mask. Ia tidak bisa meluas melampaui batas area tersebut. Karena itu sebagian area kecil, seperti 'topi' sang penyihir pada contoh sebelumnya, tidak menangani glitter dengan baik. Sparkle berbeda, sebab animasi yang ditambahkan sebagian besar transparan; akibatnya gambar aslinya masih bisa tembus terlihat. Animasi semacam itu biasanya ditambahkan ke gambar dengan salah satu dari dua cara. Entah overlay animasinya sendiri yang transparan, atau ia berbentuk latar belakang hitam dengan 'percikan' putih di tempat gambarnya perlu dicerahkan.
Sedang dikerjakan
Berikut contoh overlay 'sparkle' yang sebagian besar transparan. Contoh di sini Seperti terlihat, overlay sparkle bisa berwarna-warni bila bentuk ini yang dipakai. Masalah utamanya, animasi GIF dipakai untuk menyimpannya (dan itu memang lazim), sehingga overlay-nya beralias parah. Artinya, ia tidak bisa mengandung piksel semitransparan untuk menghaluskan tampilan gambar yang ditimpakan. Kalaupun bisa, hasil akhirnya akan punya halo hitam yang mengerikan di sekeliling 'sparkle'-nya. Mari kita mask lalu timpakan ini ke sang penyihir. Contoh di sini Bentuk sparkle yang lain adalah sparkle putih di atas latar belakang hitam (gambar grayscale). Sparkle seperti ini di-mask lalu ditimpakan untuk mencerahkan gambar sehingga muncul kilaunya. Sebagai contoh... Contoh di sini Salah satu hal terbaik tentang sparkle adalah Anda bisa membangkitkan deretan frame yang membuat sparkle-nya muncul perlahan lalu menghilang. Ini bisa jadi cukup rumit, tetapi tidak terlalu sulit dikerjakan. Contoh di sini
Menambahkan animasi flare dan bintang
Kalau glitter terdiri atas titik-titik terang tunggal, dan sparkle dapat menimpa sebagian area gambar, flare biasanya ditambahkan satu per satu. 'Flare' pada dasarnya adalah titik yang berkilat menutupi area luas hanya sesaat. 'Bintang' serupa, hanya saja cakupannya lebih berbentuk 'pancaran' cahaya. Keduanya biasanya 'disemai' dari titik tertentu, tetapi hasilnya sering meluas, setidaknya sesaat, jauh melampaui area penyemaian. Sebagai contoh, flare yang dibatasi mask ke area tertentu terlihat sangat sangat konyol dan tidak alami. Aspek yang lebih sulit dari flare adalah menemukan titik 'semai' yang bagus dan mengatur waktu beberapa flare secara pas.
Sedang dikerjakan
Contoh terakhir yang ingin saya buat... Sebuah 'sparkle' yang menjalar naik di
tongkat sang penyihir, lalu menyala terang, dan luruh menjadi sejumlah flare sparkle
kecil di suatu area. Setelah itu urutannya berulang.
Mengubah ukuran animasi
Masalah saat mengubah ukuran animasi
Masalah terbesar dalam mengubah ukuran animasi GIF adalah bahwa operator "-resize" dirancang khusus untuk membuat gambar hasilnya sedekat mungkin dengan yang ideal (setelah resize). Caranya dengan memadukan dan membangkitkan banyak warna tambahan pada gambar supaya tampak lebih bagus. Gambar hasilnya justru jauh dari ideal untuk disimpan ke format file GIF yang serba terbatas. Dengan tabel warna GIF yang terbatas, ini berujung pada reduksi warna besar-besaran pada gambar hasil resize. Untuk satu gambar GIF hal itu tidak terlalu buruk, tetapi untuk animasi GIF, dithering koreksi error bawaan atas himpunan warna yang sudah dikurangi itu menimbulkan masalah berupa 'dither noise' antar-frame, yang pada gilirannya membuat optimisasi frame buruk sehingga ukuran file akhirnya membengkak. Keadaannya makin buruk bila warna transparan ikut dipakai, dan itu praktik yang lazim pada animasi GIF untuk halaman web. Transparansi juga umum dipakai untuk teknik optimisasi kompresi, pada animasi yang sebetulnya tidak membutuhkannya. Yang terjadi adalah "-resize" menghasilkan piksel semitransparan pada gambar overlay. Lalu ketika gambar itu disimpan kembali ke format file GIF, piksel tersebut diubah menjadi sepenuhnya transparan atau sepenuhnya opak, dan keduanya menimbulkan distorsi warna besar pada animasi hasilnya. Bila optimisasi dalam bentuk apa pun dipakai... frame, transparansi, atau LZW... efek transparansi itu pada dasarnya akan menghasilkan animasi GIF hasil resize yang berantakan parah. Begitulah kenyataannya, Jack! Jadi Anda harus menerimanya. Bahkan bila Anda menghindari "-resize" dengan memakai "-sample", masalah besar tetap muncul kecuali animasinya di-"-coalesce" lebih dulu.
Teknik mengubah ukuran animasi
Seperti ditunjukkan di atas, ada masalah serius dalam mengubah ukuran animasi GIF, dan tak satu pun mudah dipecahkan. Solusinya juga umumnya bergantung pada jenis gambar apa yang diubah ukurannya sejak awal, apakah mirip kartun atau gambar video dunia nyata. Berikut metode-metode yang saya ketahui, atau yang disumbangkan orang lain...
Hindari resize
Bila memang memungkinkan, JANGAN LAKUKAN RESIZE. Anda misalnya bisa memakai crop kanvas atau viewport pada animasi untuk sekadar memangkasnya agar muat di ruang yang Anda butuhkan. Atau, animasi GIF-nya dibangkitkan langsung pada ukuran yang tepat sejak awal. Kedua teknik itu biasanya bukan pilihan terbaik, tetapi kalau bisa, pertimbangkanlah, sebab keduanya menghindarkan banyak masalah dan rasa frustrasi.
Resize langsung
Seperti disebut tadi, memakai "-resize" secara langsung akan bermasalah, entah pada jumlah warna tiap frame, atau pada warna semitransparan. Contoh berikut hasilnya benar-benar buruk... |
magick script_k.gif -resize 20x20 script_k_direct.gif
![[IM Output]](../static/img/images/script_k.gif)
![[IM Output]](../static/img/anim_mods/script_k_direct.gif)
Nah, hasilnya sama sekali tidak bagus, dan itu karena gambar aslinya punya optimisasi frame yang berat. Setiap 'frame' animasi tidak berukuran sama, dan "-resize" mengubah ukuran tiap gambar frame sepenuhnya terpisah dari gambar lainnya. Artinya, perintah di atas mengubah ukuran gambar frame yang sebenarnya, bukan kanvas virtual animasi, ke ukuran yang diberikan. Sebenarnya saya heran animasi hasilnya tidak lebih 'gila' daripada sekadar area kosong yang terlihat itu. Ini membawa kita ke poin pertama tentang mengubah ukuran animasi. Pastikan dulu semua frame terdefinisi penuh dan SELURUH optimisasi sudah dihapus. Dengan kata lain, coalesce animasinya sebelum mencoba mengubah ukurannya. Sebagai contoh... |
magick script_k.gif -coalesce -resize 20x20 script_k_direct2.gif
![[IM Output]](../static/img/images/script_k.gif)
![[IM Output]](../static/img/anim_mods/script_k_direct2.gif)
Masalah berikutnya adalah warna transparansi. Kalau Anda melihat hasil di atas, tepi animasi yang lebih kecil itu beralias parah ('bertangga'). Sebabnya, GIF tidak bisa menyimpan warna semitransparan yang dihasilkan operator "-resize". Warna-warna di dalam objek beranimasi juga akan dipadukan menjadi warna baru, tetapi biasanya itu tidak separah alias di tepinya.
Resize dengan flatten, sebuah solusi umum.
Gagasan terbaik saat membuat thumbnail GIF adalah menghindari masalah transparansi sama sekali. Artinya, ratakan animasinya, entah sebelum atau sesudah ukurannya diubah. Dengan begitu 'anti-aliasing' tepi pada gambar hasil resize tidak hilang. Nyatanya saya melihat kebanyakan situs animasi GIF yang bagus melakukan persis itu ketika membuat thumbnail animasi GIF mereka. Tentu saja thumbnail-nya lalu terbatas pada latar belakang berwarna tertentu, biasanya 'putih', kadang 'hitam', atau 'perak' (abu-abu halaman web) meski yang terakhir sudah jarang belakangan ini. Sebagai contoh, di sini saya membuat thumbnail yang lebih kecil di atas warna latar belakang yang cocok untuk halaman web ini. |
magick script_k.gif -coalesce \
-bordercolor LightSteelBlue -border 0 \
-resize 20x20 -layers Optimize script_k_thumbnail.gif
![[IM Output]](../static/img/images/script_k.gif)
![]()
Inilah solusi yang disarankan untuk penanganan thumbnail GIF secara umum. Metode lain mana pun menuntut kendali manusia, atau logika penanganan thumbnail GIF yang sangat canggih.
Ledakan tabel warna
Masalah terbesarnya (seperti saya sebut di awal bagian ini) adalah begitu banyaknya warna ekstra yang terbangkit pada gambar, terutama di dekat garis dan di tepi antara area warna yang bersebelahan. Anda juga mendapat halo hasil resize berupa warna semitransparan di sekeliling tepi gambar. Ini pada gilirannya memperbesar ukuran tabel warna yang dibutuhkan untuk animasi sederhana yang warnanya minimal, dan itu berarti ukuran file yang lebih besar ketika animasi sederhana hasil resize disimpan. Lebih buruk lagi, setiap frame dalam animasi hasil resize kemungkinan membangkitkan himpunan warna yang berbeda, sehingga ukuran file animasi 'ber-thumbnail' Anda makin membengkak. Ada pula masalah bahwa setelah kuantisasi warna, warna spesifiknya mungkin tidak lagi sama dengan animasi aslinya (lihat kuantisasi TIDAK mempertahankan warna). Artinya, alih-alih area putih murni yang sederhana, sekarang Anda bisa mendapat area putih yang agak kotor.
Resize memakai sample
Untuk menghindari terbangkitnya warna ekstra saat mengubah ukuran, cara paling sederhana adalah men-"-sample" animasinya, bukan me-resize-nya. Cara ini mempertahankan warna yang ada pada animasi dan memudahkan Anda mengoptimisasi ulang animasi pada ukuran barunya. |
magick script_k.gif -coalesce -sample 20x20 script_k_sample.gif
![[IM Output]](../static/img/images/script_k.gif)
![[IM Output]](../static/img/anim_mods/script_k_sample.gif)
Namun meski cara ini berhasil, pada dasarnya Anda membuang baris dan kolom piksel dari gambar, sehingga data gambar dan karenanya kualitasnya ikut hilang. Pada gambar mirip kartun, hal itu kerap menyisakan tepi 'bertitik-titik' serta detail yang hilang atau terdistorsi. Bila resize Anda lebih dari 50% ukuran asli animasi, seperti kasus di atas, hasilnya sering cukup mengerikan, apalagi bila animasinya memakai tekstur atau pola warna lain. Tidak mengherankan kalau banyak pustaka animasi GIF penuh dengan animasi hasil sample-resize yang mengerikan seperti itu, yang mereka salin dari seluruh penjuru internet. Saya sering berharap mereka membersihkan sampah semacam ini, tetapi itu berarti mengurangi jumlah GIF yang ditawarkan, yang pada gilirannya menurunkan statistik pemasaran soal berapa banyak GIF yang tersedia, dan bagian iklan tidak menyukai itu. Akibatnya, animasi GIF yang jelek merajalela.
Resize memakai liquid resize
Metode yang mirip dengan sample di atas adalah memakai liquid rescale, yang juga dikenal sebagai seam carving. Cara ini juga membuang atau menambahkan piksel utuh dari gambar yang terlibat, tetapi berusaha melakukannya dengan cara yang mempertahankan sebanyak mungkin kerumitan gambar. Lihat tautan di atas untuk mengetahui cara memakainya guna menghasilkan gambar hasil resize yang lebih bagus. Sayangnya saat ini belum ada cara memakainya pada gambar beranimasi secara umum, sebab ia tidak memahami kerumitan sebuah gambar, dan kita belum bisa mengeluarkan metode rescaling-nya untuk diterapkan ke tiap frame animasi secara konsisten. Semoga suatu saat nanti keadaan ini berubah.
Resize lalu pulihkan warnanya
Men-sample sebuah animasi hanya berujung pada terbuangnya baris dan kolom piksel, plus kemungkinan hilangnya garis tipis dan detail penting lainnya. Namun memadukan piksel memakai "-resize" menghasilkan terlalu banyak warna baru bagi format GIF. Jadi solusi yang jelas adalah menjalankan "-resize" lalu memakai warna animasi aslinya untuk memulihkan warna animasi hasil resize, dengan bantuan colormap.
FUTURE: example with original color table restored
Keuntungan tambahannya, tabel warna lokal tidak ikut terbangkit. Namun hasilnya mungkin lebih baik bila dither dimatikan, supaya terhindar dari 'dither noise'. Ini terutama berlaku untuk gambar mirip kartun yang punya area berwarna halus yang luas.
FUTURE: non dithered color table restored example
Optimisasi warna penuh
Bila thumbnail hasil sample yang mengerikan itu tidak Anda sukai, Anda dihadapkan pada prospek menjalani optimisasi warna penuh atas animasi GIF hasil resize, untuk memilah warna 'baru' mana saja yang ingin dipertahankan animasinya. Untuk sebagian besar animasi hal ini sering tidak terlalu berat, tetapi bisa menjadi kerja besar untuk animasi yang lebih rumit, misalnya saat mengonversi video ke animasi GIF. Artinya, kalau yang Anda hadapi animasi mirip kartun, garis dan tepinya kini akan mengalami anti-aliasing yang berat. Untuk animasi yang melibatkan latar belakang transparan, Anda juga harus menangani dengan benar piksel semitransparan di sekeliling tepi animasi, yang juga disebabkan fitur anti-aliasing pada resize. Lihat bagian transparansi boolean GIF untuk berbagai metode yang bisa dipakai menanganinya.
Pengecilan ukuran besar-besaran
Ketika Anda berencana mengecilkan animasi besar menjadi animasi yang jauh lebih kecil, muncul masalah hilangnya bagian-bagian penting dari animasi itu. Sebenarnya ini masalah bagi gambar statis maupun animasi. Lihat mengubah ukuran gambar garis untuk solusi yang sudah diketahui.
Saran, gagasan, atau teknik lain sangat saya nantikan.
Menggabungkan banyak animasi
Saya sudah menyebutnya tadi, tetapi hal ini menjadi sangat penting saat menggabungkan animasi...
Ketahui sebanyak mungkin tentang animasi yang sedang Anda kerjakan!
Skrip "gif2anim" ideal untuk keperluan ini. Skrip saudaranya, "anim2gif", juga lazim dipakai di sini untuk membuat ulang animasi memakai pengaturan aslinya. (Lihat penggunaan dasar skrip itu di informasi daftar animasi.) Tanpa pengetahuan tentang cara kerja sebuah animasi, menggabungkannya dengan berbagai cara nyaris mustahil. Program untuk itu bisa saja dikembangkan (dan itulah tujuan akhir contoh-contoh ini). Namun program semacam itu sering sangat rumit dan dapat memberi hasil yang tidak terduga. Karena itu Anda tetap sebaiknya mengikuti contoh-contoh ini, sebab semuanya memberi wawasan besar tentang bagaimana animasi seharusnya ditangani dan digabungkan.
Penyambungan serial atau menurut waktu
Menyambungkan dua animasi GIF sehingga satu urutan mengikuti urutan lain menurut waktu itu sederhana dengan IM. Pada dasarnya Anda cukup mendaftar keduanya di baris perintah dan keduanya akan saling menyusul. Namun hal ini mungkin tidak semudah kelihatannya. Sebagai contoh, setelah menjelajah web, saya menemukan (yah, mencuri lalu memodifikasinya habis-habisan untuk contoh ini) sepasang animasi huruf yang sedang ditulis. Sekarang saya ingin menyatukan gambar-gambar ini supaya begitu satu animasi selesai, animasi berikutnya dimulai, seolah-olah ada orang yang sedang menulis kata 'OK'. Berikut huruf-hurufnya, 'urutan animasi'-nya, dan detail bagian dalam kedua animasi tersebut. | |
gif2anim -n script_o.gif
gif2anim -n script_k.gif
| ![[IM Text]](../static/img/anim_mods/script_o.anim.gif)
| | ![[IM Text]](../static/img/anim_mods/script_k.anim.gif)
![[IM Output]](../static/img/images/script_o.gif)
![[IM Output]](../static/img/images/script_k.gif)
Urutan-urutan ini dimulai dari kanvas kosong lalu perlahan menambah dan mengubah piksel pada kanvas itu. Keduanya tidak pernah menghapus, membersihkan, atau membuat transparan piksel yang ditambahkan frame sebelumnya. Namun untuk keperluan kita, tidak jadi soal apakah keduanya melakukannya atau tidak, sebab pengaruhnya kecil terhadap hasil. Jumlah frame dalam animasi juga tidak berpengaruh pada operasi ini. Yang penting diketahui adalah pengaturan waktu frame-nya, karena hal itu bisa menimbulkan masalah. Perhatikan khususnya jeda waktu pada frame pertama, atau dalam kasus ini frame terakhir. Teknik ini sangat lazim, karena memberi waktu bagi penonton untuk melihat hasil akhirnya sebelum animasi terhapus dan mulai lagi. Jeda dan frame semacam inilah yang akan menyulitkan kita saat melakukan penyambungan menurut waktu. Perhatikan juga bahwa animasi 'k' punya jeda kecil di tengah urutan animasinya. Jeda ini menandai akhir sapuan kuas pertama dan awal sapuan kedua pada animasi tersebut. Jeda ini juga perlu dipertahankan, artinya kita tidak bisa begitu saja mengubah semua urutan waktu dalam animasi menjadi satu nilai tetap. Hal yang tidak tampak di atas adalah bahwa frame pertama kedua animasi sebenarnya kanvas kosong. Kanvas itu pada animasi kedua kemungkinan ingin kita buang karena hanya membuang waktu, meski pada animasi pertama sebaiknya dipertahankan sebagai jeda awal. Setelah kedua animasi kita periksa, mari kita coba menyatukan keduanya supaya yang satu mengikuti yang lain menurut waktu. Menyambung animasi menurut waktu sebenarnya operasi yang sangat sederhana: cukup sambungkan kedua gambar beranimasi itu di baris perintah. Jadi mari kita coba saja... |
magick script_o.gif script_k.gif script_ok_try1.gif
![[IM Output]](../static/img/anim_mods/script_ok_try1.gif)
Yah, hasilnya jauh dari sempurna. Huruf-hurufnya memang digambar dalam urutan yang benar, tetapi saling bertumpuk! Bukan hanya itu, karena animasi 'o' pertama lebih sempit (40 piksel) daripada animasi 'k' kedua (53 piksel), bagian paling ujung huruf 'k' terakhir terpotong oleh ukuran kanvas yang lebih kecil itu. Posisi animasi kedua dapat digeser memakai relative repage seperti ditunjukkan di atas. Metode pemindahan posisi ini mempertahankan offset yang mungkin sudah ada pada animasi tersebut, dan hanya menggeser semuanya sebagai satu kelompok yang berkaitan. Dalam kasus ini hampir semua frame sudah punya offset, sebab animasi ini sangat teroptimisasi. Untuk menampung posisi yang bergeser itu dan menghindari 'terpotongnya' animasi kedua, ukuran kanvas keseluruhan animasi juga perlu diperbesar. Mengubah ukuran kanvas sebelum animasi atau frame pertama dibaca akan memperluas area kanvas tempat animasi berjalan, sekaligus mencegah huruf 'K' terpotong. |
magick -page 90x54 script_o.gif \
\( script_k.gif -repage +37+0\! \) script_ok_try2.gif
![[IM Output]](../static/img/anim_mods/script_ok_try2.gif)
Hasilnya jauh lebih baik. Meski begitu jeda antara penulisan kedua huruf kini terasa jelas. Yang kita inginkan adalah jeda yang jauh lebih pendek untuk frame terakhir animasi 'O' pertama, cukup panjang saja supaya tampak seolah sang seniman tak terlihat sedang memindahkan penanya. Untuk itu kita membuat salinan frame terakhir animasi pertama, lalu mengubah jeda frame itu saja memakai operator "-set". Frame tersebut lalu kita kembalikan ke dalam urutan gambar dengan menghapus gambar asli yang belum dimodifikasi. Selain itu, karena jeda antara penulisan kedua huruf sudah kita setel dengan baik, kanvas kosong di awal (yang cuma mewakili jeda awal) pada animasi kedua kini mubazir, jadi frame itu bisa langsung dihapus tanpa masalah. Kalau frame ini sebenarnya memuat sebagian gambar, kita mungkin perlu menyesuaikan jedanya alih-alih menghapusnya. |
magick -page 90x54 script_o.gif \( +clone -set delay 20 \) -delete -2 \
\( script_k.gif -delete 0 -repage +37+0\! \) script_ok.gif
![[IM Output]](../static/img/anim_mods/script_ok.gif)
Dan penyambungan serial atau menurut waktu atas dua animasi pun selesai, dengan semua masalah kecil khas kedua animasi ini sudah teratasi. Perhatikan bahwa saya sama sekali tidak pernah berusaha mengubah SEMUA frame atau jeda waktunya secara global. Artinya, saya mempertahankan sebanyak mungkin animasi aslinya sambil tetap mencapai tujuan saya. Ini penting, sebab tidak semua animasi memakai jeda waktu yang tetap antar-frame, dan mengubahnya bisa membuat animasi terlihat sangat buruk.
Penyambungan berdampingan (tersinkron waktu)
Misalkan Anda ingin kedua animasi disambungkan berdampingan, tetapi kedua bagiannya beranimasi pada saat yang sama. Ini tidak begitu mudah, sebab Anda harus menyambungkan (atau mengomposisikan) tiap pasang frame dari kedua animasi tersebut supaya animasinya juga berjalan bersamaan. Masalah sebenarnya, baris perintah IM hanya bekerja dengan satu urutan gambar. Ia tidak punya kemewahan API yang membiarkan Anda menyimpan dua urutan gambar terpisah, lalu menelusurinya dan menyambungkannya menjadi urutan ketiga. Ada tiga teknik dasar yang terpikir oleh saya untuk melakukan penyambungan ini. Namun sebelum mulai, pelajari dulu kedua animasinya untuk memeriksa urutan waktu dan detail lainnya. Skrip "gif2anim" cocok untuk itu, dan file ".anim' yang dihasilkannya bisa berguna nanti. | |
gif2anim -n bag_left.gif
gif2anim -n bag_right.gif
| ![[IM Text]](../static/img/anim_mods/bag_left_orig.anim.gif)
| | ![[IM Text]](../static/img/anim_mods/bag_right_orig.anim.gif)
![[kiri]](../static/img/images/bag_left.gif)
![[kanan]](../static/img/images/bag_right.gif)
Kalau Anda melihat ringkasan informasinya, kedua animasi itu punya jumlah frame yang persis sama, dan urutan waktu yang hampir persis sama pula. Kemiripan pengaturan waktunya itulah yang penting di sini, dan bisa dibilang kedua animasi sudah 'tersinkron waktu'. Namun meski waktunya sudah benar, kedua animasi itu dioptimisasi frame-nya, bukan sepenuhnya hasil coalesce. Tinggi area kanvasnya sama, jadi menyambungkan kedua frame secara berdampingan itu praktis. Sebenarnya animasi ini dulu 'dipecah' dengan buruk (lihat memecah animasi pada kumpulan contoh berikutnya) sehingga animasi 'kucing'-nya terbelah dua dan aslinya hilang. Modifikasi lain menyisakan selisih waktu yang sangat tipis, yang justru membuat pembelahannya makin kentara. Masalah ini disodorkan kepada saya oleh gmabrys dalam diskusi di IM Forums, meski masalah yang sebenarnya ia berikan jauh, jauh lebih parah. ![[kiri]](../static/img/images/bag_right.gif)
Nah, peramban biasanya menganimasikan tiap gambar GIF terpisah tanpa sinkronisasi apa pun. Akibatnya kedua animasi bisa saling melenceng waktunya, sehingga muncul 'kucing' yang tampak seperti korban pembantaian gergaji mesin. Efek ini mungkin bisa Anda lihat di sebelah kanan, tempat saya menaruh dua animasi berdampingan pada halaman peramban, terutama bila Anda mengaksesnya dari server yang jauh lewat jalur yang lambat. Sekarang mari kita coba menyambungkan keduanya menjadi satu gambar beranimasi yang tersinkron dengan benar.
Menyambungkan file terpisah
Cara paling sederhana adalah meng-coalesce kedua animasi lalu memisahkannya menjadi file gambar terpisah, satu frame per gambar. Gambar-gambar terpisah itu kemudian bisa disambungkan (atau frame-nya dimodifikasi dengan cara lain) sesuai kebutuhan. Setelah selesai, frame-frame baru itu dipakai untuk membangun ulang animasinya. Namun cara ini menuntut Anda menyimpan banyak informasi tambahan tentang animasi, yang sangat mudah hilang selama pemrosesan ini. |
# Separate animations into coalesced frames (plus a ".anim" file)
gif2anim -c bag_left.gif
gif2anim -c bag_right.gif
# Append the separated frames them together
for i in `seq -f '%03g' 1 11`; do \
magick bag_left_$i.gif bag_right_$i.gif +append bag_append_$i.gif; \
done
# Rebuild the animation (using one of the ".anim" files)
anim2gif -c -b bag_append bag_left.anim
# Cleanup
rm -f bag_left.anim bag_right.anim
rm -f bag_{left,right,append}_???.gif
![[IM Output]](../static/img/anim_mods/bag_append_anim.gif)
Seperti terlihat, prosesnya cukup berbelit, membangkitkan banyak gambar sementara satu per satu, sehingga perlu cukup banyak pembersihan setelah selesai. Tentu saja bila Anda sedang men-debug perintah di atas, file sementara satu per satu itu memudahkan Anda melacak apa yang salah pada pemrosesan Anda. Ini juga menunjukkan kekuatan skrip "gif2anim" beserta kebalikannya, skrip "anim2gif", dalam memisahkan dan menyimpan meta-data animasi, lalu membangun ulang animasi GIF kemudian. Pada dasarnya keduanya membiarkan Anda mempertahankan pengaturan waktu asli animasi tanpa perlu menuliskannya langsung ke dalam skrip Anda. Gambar akhirnya juga masih perlu dioptimisasi ulang, meski dalam kasus ini optimisasinya sangat sedikit karena banyak hal terjadi serentak di sepanjang animasi antara setiap frame
Komposisi berlapis
Teknik yang lebih baik adalah menimpakan animasi memakai komposisi layer atas daftar multi-gambar. Caranya cukup dengan memperbesar satu kumpulan gambar, lalu menimpakan kumpulan yang lain untuk menyatukannya. Sebenarnya itulah yang dikerjakan operator "-append" biasa di dalamnya, jadi tidak jauh berbeda. Di sini saya sekadar memberi tahu IM seberapa besar kanvasnya harus dibuat, lalu mengisinya penuh memakai "-coalesce". Setelah itu saya menimpakan animasi hasil coalesce yang lain dengan offset yang sesuai. |
magick bag_left.gif -repage 97x92 -coalesce \
null: \( bag_right.gif -coalesce \) \
-geometry +50+0 -layers Composite bag_append.gif
![[IM Output]](../static/img/anim_mods/bag_append.gif)
Tentu saja teknik di atas berarti saya perlu tahu seberapa besar animasi akhirnya, sekaligus offset yang dibutuhkan animasi yang ditimpakan. Namun prosesnya cepat, hasilnya bagus, dan sebuah perintah berskrip dapat membaca gambarnya lebih dulu untuk menentukan informasi tersebut. Untuk membuat metode penyambungan animasi yang lebih universal, kita perlu penanganan gambar yang agak canggih guna menentukan ukuran akhir dan offset penyambungan secara otomatis. Melakukannya tanpa membaca animasinya lebih dulu memang butuh sedikit akrobat, tetapi penyambungan animasi umum dalam satu perintah tetap mungkin. Pertama kita perlu menyambungkan frame hasil coalesce pertama dari tiap animasi untuk membuat kanvas berukuran tepat, lalu kanvas itu dikosongkan. Animasi pertama di-coalesce dan ditimpakan ke separuh kiri kanvas ini, lalu animasi kedua di-coalesce dan ditimpakan dengan "-gravity East" agar berada di separuh paling kanan kanvas yang sudah disiapkan tadi, sehingga offset tidak diperlukan. |
magick bag_left.gif'[0]' -coalesce \( bag_right.gif'[0]' -coalesce \) \
+append -channel A -evaluate set 0 +channel \
bag_left.gif -coalesce -delete 0 \
null: \( bag_right.gif -coalesce \) \
-gravity East -layers Composite bag.gif
![[IM Output]](../static/img/anim_mods/bag.gif)
Dan begitulah teknik umum untuk menyambungkan dua animasi yang tersinkron waktu.
Penyambungan ganda, atau menyambungkan font beranimasi
Sebelum menutup pembahasan penyambungan animasi, ada satu teknik lain yang ingin saya tunjukkan. Teknik ini bisa menyambungkan beberapa animasi sekaligus, tetapi dengan ongkos hilangnya seluruh informasi waktu yang ada. Sering (walau tidak selalu) hilangnya pengaturan waktu itu bukan kerugian besar. Pada dasarnya kita menyambungkan semua frame tiap animasi secara vertikal menjadi satu gambar, lalu menyambungkan atau menimpakan keseluruhan animasi itu sebagai dua gambar biasa. Kira-kira seperti merekatkan dua gulungan film berdampingan untuk menghasilkan gulungan film yang lebih lebar. |
magick \( bag_left.gif -coalesce -append \) \
\( bag_right.gif -coalesce -append \) \
+append -crop x92 +repage \
-set delay 30 bag_dbl_append.gif
![[IM Output]](../static/img/anim_mods/bag_dbl_append.gif)
Cara ini tidak membutuhkan file sementara sama sekali, tetapi seperti saya sebut di awal, semua jeda waktu aslinya hilang. Untuk contoh ini saya sekadar menyetel semua jeda animasi ke satu nilai tetap, dan hasilnya cukup masuk akal meski berbeda. Selain itu, untuk membangun ulang animasinya kita perlu tahu tinggi frame animasi aslinya agar 'gulungan film' yang melebar itu terbagi dengan benar (tile crop). Pengaturan waktunya memang bisa dipulihkan memakai skrip "gif2anim", tetapi melakukannya agak menggugurkan tujuan pemakaian metode ini, dan Anda sekalian saja memakai teknik penyambungan animasi yang pertama, yaitu menyambungkan tiap frame sebagai file sementara. Karena animasinya disambungkan sebagai gambar biasa, Anda bisa menyambungkan serangkaian animasi sekaligus (menghasilkan 'gulungan film' yang lebih lebar lagi), dan itulah yang membuat teknik ini begitu berguna. Anda misalnya bisa memakainya untuk font beranimasi yang semuanya memakai pengaturan waktu yang sama. Meski begitu saya menemukan bahwa walaupun banyak font beranimasi punya jumlah frame yang sama, biasanya waktunya sedikit berbeda untuk tiap huruf supaya huruf-huruf beranimasi itu tidak tersinkron (lihat memecah sebuah animasi untuk alasan mengapa hal itu diinginkan). Papan neon, sebaliknya, semestinya punya pengaturan waktu animasi yang tersinkron, jadi saya akan memakainya sebagai contoh...
magick \( neon_h.gif -coalesce -append \) \
\( neon_e.gif -coalesce -append \) \
\( neon_l.gif -coalesce -append \) \
\( neon_l.gif -coalesce -append \) \
\( neon_o.gif -coalesce -append \) \
+append -crop x60 +repage -set delay 100 neon_hello.gif
Anda juga bisa melakukan sesuatu yang sedikit lebih canggih, dengan menyesuaikan pengaturan waktu dan jumlah loop pada animasi hasilnya.
magick neon_h.gif'[0]' neon_e.gif'[0]' neon_l.gif'[0]' neon_l.gif'[0]' \
+append \( +clone \) -append \
\( neon_o.gif -coalesce -append \) +append \
\( +clone \) -append \( +clone \) -append \( +clone \) -append \
-crop x60 +repage -set delay 3 \
\( -clone 0 -set delay 300 \) -swap 0,-1 +delete \
\( -clone 1 -set delay 10 \) -swap 1,-1 +delete \
\( +clone -set delay 200 \) +swap +delete \
-quiet -layers OptimizeFrame neon_hell.gif
Dua baris pertama membuat bagian papan yang 'selalu menyala' (frame pertama dari tiap huruf yang tadinya beranimasi). Setelah itu huruf 'rusak' yang terakhir ditambahkan, dan keseluruhan animasi digandakan beberapa kali sehingga menghasilkan sekitar 16 frame. Pengaturan waktunya disetel untuk mencapai efek yang diinginkan, dengan frame pertama dan terakhir ditampilkan lama, sementara sisanya berkelebat sangat cepat ( "-delay 10" ). Sebenarnya animasi GIF ini teroptimisasi jauh lebih kecil daripada yang mungkin Anda kira untuk jumlah frame sebanyak itu. Pada dasarnya pengoptimal GIF IM mendapati bahwa ia hanya perlu menimpakan ulang animasi 'O' setiap frame kedua, dan memakai disposal 'Previous' untuk sekadar memulihkan 'O' menyala sebelumnya. Jadi animasinya hanya sekitar 50% lebih besar daripada gambar 'hello' berkedip yang tidak dioptimisasi. Silakan periksa sendiri. Bisakah Anda memperbaiki animasi neon ini? Membuatnya lebih realistis? Sayang sekali animasi GIF tidak punya suara.
Memecah sebuah animasi
Setelah animasinya kita satukan kembali, mari kita coba memecahnya dengan benar untuk dipakai di server web, supaya tiap bagian bisa beranimasi terpisah tanpa saling mengganggu. Ini sebenarnya cukup sulit, dan saya tidak akan berusaha mengotomatiskan prosesnya sepenuhnya. Meski begitu ada perkakas di WWW yang bisa melakukannya. Pertama-tama kita perlu mempelajari animasinya untuk mengetahui bagian mana saja yang berubah sepanjang periode itu. Untuk itu kita perlu mencari perbedaan dari satu frame ke frame berikutnya, lalu menjumlahkan semuanya menjadi peta yang menunjukkan area mana yang beranimasi dan mana yang sepenuhnya statis. Ini rumit. Pada dasarnya komposisi alpha multi-gambar dipakai untuk mencari gambar 'Difference' antara tiap frame animasi. Gambar-gambar selisih grayscale itu dijumlahkan, lalu kanalnya dipisahkan dan dijumlahkan pula. Ambang batas terakhir kemudian membuat setiap perubahan bukan nol antar-frame animasi menjadi putih murni. Hasilnya berupa gambar hitam dengan warna putih di mana pun gambarnya berubah, sehingga area perubahannya tersorot.
magick bag.gif -coalesce -set delay 0 \
-bordercolor red -border 0 -alpha off null: \
-duplicate 1,1--2 -compose difference -layers composite \
+delete -compose plus -background black -flatten \
-separate -flatten -threshold 0 bag_areas.gif
Sekarang terlihat bahwa animasi ini bisa dibagi menjadi setidaknya tiga area: area 'kucing' di bagian atas, 'beruang' kecil di sebelah kiri, dan 'sayap' yang mengepak di sebelah kanan. Semuanya dengan potongan ortogonal (vertikal atau horizontal) yang sederhana. Jadi mari kita lakukan itu, dengan beberapa viewport crop pada animasi yang sederhana. |
magick bag.gif -coalesce -crop 97x39+0+0\! bag_cat.gif
magick bag.gif -coalesce -crop 50x54+0+39\! bag_bear.gif
magick bag.gif -coalesce -crop 47x54+50+39\! bag_wing.gif
| ![[IM Output]](../static/img/anim_mods/bag_cat.gif)
| ![[IM Output]](../static/img/anim_mods/bag_wing.gif)
Ketiga gambar ini bisa ditampilkan peramban secara bersamaan tanpa berkesan 'Texas Chainsaw Massacre', sebab tidak ada satu pun sub-animasi yang melewati batas sub-animasi lain. Secara teknis Anda bisa membuat beberapa potongan lagi untuk memisahkan area yang tidak beranimasi dari area yang beranimasi, sehingga animasi ini terpecah menjadi sekitar enam area atau lebih, meski keuntungan optimisasinya tidak seberapa. Yang benar-benar terjadi hanyalah halaman web Anda menjadi rumit dan file yang harus diunduh pengguna bertambah banyak. Nah, tidak seperti animasi yang lebih besar tadi, area-area kecil ini akan beranimasi cukup mandiri satu sama lain. Kita bahkan bisa mengubah pengaturan waktu sub-animasi sederhana ini tanpa merusak hasilnya, sehingga waktunya sepenuhnya tidak lagi sinkron dengan sub-animasi yang lain. Hasilnya gambar beranimasi yang lebih enak dilihat dan tidak monoton (lihat di bawah). Kalau Anda mempelajari 'beruang yang memantul' dan 'sayap yang mengepak', keduanya membentuk siklus dua frame sederhana yang sekadar berulang beberapa kali agar cocok dengan waktu kucing yang melambai. Karena itu kita bisa membuang pengulangan berlebihnya untuk menyederhanakan animasi tersebut. Dua frame pertama 'kucing' pun persis sama. Namun berbeda dari 'beruang' dan 'sayap', salah satunya tidak bisa begitu saja dihapus, sebab tiap frame memuat jeda waktu yang memungkinkan 'beruang' dan 'sayap' beranimasi tanpa kehadiran kucingnya. Untuk menghapus frame duplikat itu dengan benar, pakai metode "-layer" 'RemoveDups' guna menemukan dan menggabungkan waktu frame duplikat semacam itu pada animasi hasil coalesce. Dan inilah optimisasi akhir ketiga animasi terpisah tersebut, lengkap dengan perubahan waktu untuk memperbaiki desinkronisasi antar-sub-animasi secara keseluruhan. Saya juga menampilkan ketiga animasi berdampingan pada halaman ini, persis seperti seharusnya ditampilkan. |
magick bag_cat.gif -layers RemoveDups \
-quiet -layers Optimize bag_cat_opt.gif
magick bag_bear.gif -delete 2--1 -set delay 47 \
-layers Optimize bag_bear_opt.gif
magick bag_wing.gif -delete 2--1 -set delay 33 \
-layers Optimize bag_wing_opt.gif
| ![[IM Output]](../static/img/anim_mods/bag_cat_opt.gif)
| ![[IM Output]](../static/img/anim_mods/bag_wing_opt.gif)
Sebagai rangkuman akhir: kedua gambar asli (yang dipecah dengan buruk) berjumlah
byte, kira-kira sama dengan versi yang disambungkan. Setelah animasinya dipecah dengan benar, yang memungkinkan sub-animasinya dioptimisasi dengan baik, totalnya menjadi
byte untuk tiga gambar. Penghematan yang lumayan.
Memecah frame untuk perubahan yang berjauhan
Sedang dikerjakan
Contoh memecah pembaruan frame untuk 'dua objek berubah yang berjauhan', tanpa melibatkan transparansi (latar belakang tetap), tetapi tetap mempertahankan sinkronisasi waktu antar-bagiannya. Lalu diulang dengan latar belakang transparan, (yang memerlukan 'OptimizePlus' untuk membangkitkan piksel yang 'terhapus'. Lihat memecah aksi frame untuk contoh umumnya.
Menggabungkan animasi yang waktunya tidak selaras
Sebelum dua animasi dapat digabungkan agar berjalan serentak, semua animasi harus dibuat memakai jumlah frame yang sama dan himpunan jeda waktu yang sama. Seberapa sulit penggabungannya sangat bergantung pada seberapa tidak selaras pengaturan waktu animasinya. Bila jeda waktunya pada dasarnya tetap, jeda itu bisa diabaikan saja dan waktunya dibereskan belakangan. Contoh kasus yang waktunya bisa diabaikan, yaitu menggabungkan animasi 2 frame dengan animasi 6 frame, diberikan dalam diskusi di IM Forum. Selain itu, bila total waktu siklusnya sangat berbeda, Anda mungkin perlu mengatur agar satu animasi mengulang 2 atau 3 kali supaya mengisi waktu siklus animasi yang lain. Pada dasarnya waktulah yang menentukan.
Mungkin kira-kira seperti ini... * + Cari tahu lalu sesuaikan animasi ke satu total waktu siklus loop yang sama * + Coalesce kedua animasi untuk menghapus optimisasi frame apa pun. * * Ubah jeda waktu frame menjadi waktu-sejak-awal animasi. * * Gandakan frame seperlunya untuk menyinkronkan waktunya. * * Ubah kembali waktu-sejak-awal menjadi jeda waktu frame. * + Timpakan frame hasil coalesce yang tersinkron waktu sesuai keinginan. * + Gabungkan secara optimal dan buang frame 'jeda nol' mana pun. * + Optimisasi ulang animasi barunya.
Bagian bertanda '*' bisa dijadikan satu metode "-layer" baru untuk menyinkronkan waktu dua animasi yang total durasi siklusnya mirip.
Sedang dikerjakan
Contoh, waktu tidak selaras, tetapi waktu siklusnya sama...
Sebagai contoh, misalkan Anda punya dua animasi tiga frame dengan jeda waktu
10 10 10
5 5 20
Kedua animasi sudah sama-sama sepanjang 30 satuan waktu, jadi itu bukan masalah.
Sekarang ubah data di atas menjadi indeks waktu saat tiap frame semestinya muncul...
lalu tampilkan garis waktu keseluruhan tempat frame-frame itu muncul...
0 10 20 |__ PERHATIKAN bahwa kedua animasi
0 5 10 | berakhir atau mengulang di 30
Dari sini terlihat bahwa Anda perlu menyisipkan beberapa frame ekstra supaya
keduanya cocok. Frame pertama animasi pertama perlu diulang pada indeks
waktu 5
0->5 10 20
0 5 10
Dan frame terakhir animasi kedua juga perlu digandakan pada indeks
waktu 20
0->5 10 20
0 5 10->20
Tanda panah '->' di atas berarti frame yang sama sekadar diulang (digandakan)
ke indeks waktu berikutnya. Keduanya sebenarnya gambar yang sama.
Setelah pengaturan waktu frame kedua animasi sama, Anda tinggal
menggabungkan (mengomposisikan) frame-frame itu untuk mendapat animasi akhir
sepanjang 4 frame.
Keempat frame itu lalu punya jeda waktu
5 5 10 10
yang tetap berjumlah 30 satuan waktu (total waktu per siklus loop)
Keadaan pengembangan saat ini....
IM memang bisa membantu mengumpulkan informasi jeda waktu (coba opsi '-t' pada
"gif2anim") dan membangun animasinya. IM tidak bisa melakukan sinkronisasi waktu
yang dibutuhkan dua animasi hasil coalesce yang terpisah. Ini mungkin kelak jadi
opsi bawaan khusus.
Artinya, Anda perlu mencari tahu dan menggandakan frame animasi hasil coalesce
yang sesuai supaya dua animasi yang waktunya tidak selaras berubah menjadi dua
animasi yang tersinkron waktu.
Begitu animasinya tersinkron waktu, Anda tinggal memakai metode
"-layers Composite" yang baru itu untuk menimpakan atau menggabungkan kedua
animasi tersinkron waktu tersebut dengan sangat mudah.
Namun semua di atas mengandaikan total waktu loop kedua animasi
setidaknya kurang lebih sama, atau bukan persoalan besar.
**Solusi yang disederhanakan**
Solusi terbatas yang disederhanakan sudah [dibahas di IM Forums](https://magick.imagemagick.org/viewtopic.php?p=50325), untuk dipakai pada animasi yang berubah cepat (similes).
Solusinya mengambil tiap animasi lalu mengembangkannya supaya animasi itu punya
frame rate yang tetap. Artinya, semua frame digandakan supaya tiap frame
ditampilkan selama 6 senti-detik yang tetap. Dengan begitu satu frame berjeda 22cs
bisa digantikan 4 x frame 6cs (total 24cs).
Setelah itu animasinya dimodifikasi lebih lanjut supaya animasi yang pendek
diulang beberapa kali sehingga kedua animasi akhirnya sama panjang.
Artinya, kedua animasi dibuat sama panjang secara keseluruhan, baik dalam hal
waktu maupun jumlah frame.
Begitu kedua animasi punya frame rate dan panjang yang sama, [komposisi layer](#composite) dapat dipakai untuk menggabungkan/menimpakan kedua
animasi itu pada posisi yang tepat.
Hasilnya kemudian bisa dioptimisasi memakai [menghapus frame duplikat](anim_opt.html#removedups) untuk membuang frame ekstra yang tidak diinginkan (dengan
penyesuaian waktu yang sesuai dan [optimisasi](anim_opt.html#intro) lain yang diterapkan sebelum disimpan.
Metode menyimpan semua animasi komponen Anda dalam bentuk panjang frame tetap ini
sangat cocok untuk pustaka animasi.
-----
Contoh lain yang ingin dibuat....
* Menimpakan dua animasi bergerak berdurasi sama menjadi satu animasi
(kupu-kupu menari, atom yang mengorbit, atau burung?)
Ini semestinya sekadar komposisi layer biasa.
* Menimpakan animasi bergerak di atas latar belakang tetap.
(geser animasinya secara linear terhadap waktu)
* Menimpakan dua animasi dengan jumlah frame berbeda tetapi jeda waktu
tetap (lihat [diskusi di IM Forum](https://magick.imagemagick.org/viewtopic.php?t=12573)).
* Menimpakan dua animasi yang waktunya tidak selaras (seperti diuraikan di atas)
* Menimpakan figur beranimasi sederhana di atas latar belakang beranimasi.
(penggabungan animasi penuh)
![[IM Output]](../static/img/anim_mods/annotate.gif)
![[IM Output]](../static/img/anim_mods/annotate_frames.gif)
![[IM Output]](../static/img/anim_mods/draw_circle_fail.gif)
![[IM Output]](../static/img/anim_mods/draw_circle_fail_frames.gif)
![[IM Output]](../static/img/anim_mods/draw_circle.gif)
![[IM Output]](../static/img/anim_mods/draw_circle_frames.gif)
![[IM Output]](../static/img/anim_mods/frame_mod.gif)
![[IM Output]](../static/img/anim_mods/frame_mod_frames.gif)
![[IM Output]](../static/img/anim_mods/cropped_frames.gif)
![[IM Text]](../static/img/anim_mods/cropped_error.txt.gif)
![[IM Output]](../static/img/anim_mods/crop_viewport_frames.gif)
![[IM Output]](../static/img/anim_basics/anim_bgnd.gif)
![[IM Output]](../static/img/anim_mods/anim_trim_bounds.gif)
![[IM Text]](../static/img/anim_mods/anim_trim_bounds.txt.gif)
![[IM Output]](../static/img/anim_mods/anim_trim_crop.gif)
![[IM Output]](../static/img/anim_mods/patrol_cycle_2.gif)
![[IM Output]](../static/img/anim_mods/wipe_all.gif)
![[IM Output]](../static/img/anim_mods/coalesced_k.gif)
![[IM Output]](../static/img/anim_mods/shadows_k.gif)
![[IM Output]](../static/img/anim_mods/compose_shadow.gif)
![[IM Output]](../static/img/anim_mods/compose_shadow_frames.gif)
![[IM Output]](../static/img/anim_mods/glitter_mask_trans.gif)
![[IM Output]](../static/img/anim_mods/glitter_masked_trans.gif)
![[IM Output]](../static/img/anim_mods/glitter_mask.gif)
![[IM Output]](../static/img/anim_mods/glitter_masked.gif)
![[IM Output]](../static/img/images/logo.gif)
![[IM Output]](../static/img/anim_mods/logo_holed.gif)
![[IM Output]](../static/img/images/neon_h.gif)
![[IM Output]](../static/img/images/neon_e.gif)
![[IM Output]](../static/img/images/neon_l.gif)
![[IM Output]](../static/img/images/neon_o.gif)
![[IM Output]](../static/img/anim_mods/neon_hello.gif)
![[IM Output]](../static/img/anim_mods/neon_hell.gif)
![[IM Output]](../static/img/anim_mods/bag_areas.gif)