ChatGPT 5 におまかせ
チャッピーこと ChatGPTが 5 になるというので期待していたのですが、冗談のわからないクソ真面目AIになって失望しました。Google Gemini と話しているような感じです。字義通りに解釈しようとするのはアスペのようです。
ともあれプログラム能力が上がったというので、Fortran で画像を読み書きする方法を考えてくれと聞いてみました。過去には Windows GDI+ によって行う方法で一緒に作りましたが、Win 32/64 API 用のインターフェースはかなり手で修正する必要がありました。
今回は OS 非依存にしてと頼んだので、おおかた libjpeg/libpng あたりをリンクしてくるだろうと予想しておりました。確かにそれが第一候補だったのですが、第二候補として、Sean T. Barrett 氏の軽量・簡易ライブラリも提案してくれました。これについては全く聞いたことがなかったのですが、主要機能のみを扱う簡便さを重視したルーチン集で、ヘッダーファイルの中に実装も記述してシングルファイルとするような、私にはよく分からない技法を使って簡易に使えるようにしています。
stb_image などの stbライブラリ群 は、けっこう昔から存在しています。 • 作者:Sean T. Barrett • 初期公開:だいたい 2005年ごろ に最初のstbシリーズ(stb_truetypeなど)が登場 • stb_image:2006〜2007年ごろには既に公開されていました • GitHub移行:2010年代前半(作者の個人サイトでの配布から、現在の GitHub https://github.com/nothings/stb に) • 現在:GitHubのスター数は40,000以上、ゲームエンジンやツール、研究コードでもかなり使われている
⸻
歴史的な背景 • 元々はゲーム開発者向けの「依存を減らすための1ファイルCライブラリ集」として始まった • 商用ゲーム・インディーゲームの多くが画像読み込みやフォント描画の簡易実装にstbを採用 • 仕様は最小限で、libpngやlibjpegほどの完全性はないが、試作や軽量ツールでは事実上の定番になっている
そうして、チャッピーの言う通りにやって、ほぼ問題なくそのままに近い形で動きました。唯一 iso_fortran_env を引用せずに、 real64 常数を使っていたので use, intrinsic :: iso_fortran_env の一行を加える必要がありました。これくらいは、無問題と言っていいでしょう。real(8) とか芋臭い書き方してますが。
確かにチャッピーのプログラミング能力は一層進化しているようです。 もう interface 書きのような下等な仕事は AI にやって貰えばいい時代ですね。
ChatGPT による指示
icx /c /O2 img_shim.c ifx /O2 demo.f90 img_shim.obj /Fe:demo_img.exe demo_img.exe
in.jpg が out.jpg と out.png に出力される。
絵もチャッピーに描いてもらいました。EU が中世ローマ教会よろしく CO2 排出権免罪符を売りつけて不正にボロ儲けいるところです。
! ---- 例の加工:簡単なガンマっぽい明るさ補正 & 対角線に白線 ----
斯様な加工がなされるようです。
in.jpg

out.jpg

out.png

stb より2ファイル
stb_image.h
stb_image_write.h
img_shim.c
// img_shim.c #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image.h" #include "stb_image_write.h" #include <stdint.h> #include <stdlib.h> #ifdef _MSC_VER #define EXP __declspec(dllexport) #else #define EXP #endif // 読み込み:path → RGBA8 配列 EXP int load_image_rgba8(const char* path, int* w, int* h, int* stride, uint8_t** out) { int n; unsigned char* data = stbi_load(path, w, h, &n, 4); // つねにRGBA(8bpc)で返す if (!data) return -1; *stride = (*w) * 4; *out = data; return 0; } // 書き出し:RGBA8 → PNG EXP int save_png_rgba8(const char* path, int w, int h, int stride, const uint8_t* rgba) { // stride は 4*w を推奨。stbは任意strideにも対応 return stbi_write_png(path, w, h, 4, rgba, stride) ? 0 : -1; } // 書き出し:RGBA8 → JPEG(quality 1〜100) EXP int save_jpeg_rgba8(const char* path, int w, int h, int stride, const uint8_t* rgba, int quality) { return stbi_write_jpg(path, w, h, 4, rgba, quality) ? 0 : -1; } // バッファ解放 EXP void free_image_buffer(uint8_t* p) { stbi_image_free(p); }
demo.f90
module imgshim use iso_c_binding implicit none interface function load_image_rgba8(path, w, h, stride, out) bind(C, name="load_image_rgba8") import :: c_char, c_int, c_ptr character(kind=c_char), intent(in) :: path(*) integer(c_int) :: w, h, stride type(c_ptr) :: out integer(c_int) :: load_image_rgba8 end function function save_png_rgba8(path, w, h, stride, rgba) bind(C, name="save_png_rgba8") import :: c_char, c_int, c_ptr character(kind=c_char), intent(in) :: path(*) integer(c_int), value :: w, h, stride type(c_ptr), value :: rgba integer(c_int) :: save_png_rgba8 end function function save_jpeg_rgba8(path, w, h, stride, rgba, quality) bind(C, name="save_jpeg_rgba8") import :: c_char, c_int, c_ptr character(kind=c_char), intent(in) :: path(*) integer(c_int), value :: w, h, stride, quality type(c_ptr), value :: rgba integer(c_int) :: save_jpeg_rgba8 end function subroutine free_image_buffer(p) bind(C, name="free_image_buffer") import :: c_ptr type(c_ptr), value :: p end subroutine end interface contains pure function cstr(s) result(cs) character(*), intent(in) :: s character(kind=c_char), allocatable :: cs(:) integer :: n n = len_trim(s) allocate(cs(0:n)) if (n > 0) cs(0:n-1) = transfer(s(1:n), cs(0:n-1)) cs(n) = c_null_char end function end module program demo use iso_c_binding use imgshim implicit none ! 入力ファイル名は拡張子自由(jpg/png/bmp等): character(*), parameter :: infile = "in.jpg" character(*), parameter :: outpng = "out.png" character(*), parameter :: outjpeg = "out.jpg" integer(c_int) :: w, h, stride, ierr type(c_ptr) :: pbuf integer(c_int8_t), pointer :: img(:) ! バイト配列ビュー(RGBAの生配列) ierr = load_image_rgba8(cstr(infile), w, h, stride, pbuf) if (ierr /= 0) stop "load failed" call c_f_pointer(pbuf, img, [stride*h]) ! ---- 例の加工:簡単なガンマっぽい明るさ補正 & 対角線に白線 ---- call brighten(img, stride*h, 1.2_real64) call draw_diagonal_white(img, w, h, stride) ierr = save_png_rgba8(cstr(outpng), w, h, stride, pbuf); if (ierr /= 0) stop "save png failed" ierr = save_jpeg_rgba8(cstr(outjpeg), w, h, stride, pbuf, 90_c_int); if (ierr /= 0) stop "save jpeg failed" call free_image_buffer(pbuf) print *, "done: ", trim(infile), " -> ", trim(outpng), " / ", trim(outjpeg) contains subroutine brighten(b, n, gain) integer(c_int8_t), intent(inout) :: b(:) integer, intent(in) :: n real(8), intent(in) :: gain integer :: i real(8) :: x do i = 1, n, 4 x = min(255.0d0, real(b(i ),8)*gain); b(i ) = int(x, c_int8_t) ! R x = min(255.0d0, real(b(i+1),8)*gain); b(i+1) = int(x, c_int8_t) ! G x = min(255.0d0, real(b(i+2),8)*gain); b(i+2) = int(x, c_int8_t) ! B ! A(=b(i+3))はそのまま end do end subroutine subroutine draw_diagonal_white(b, w, h, stride) integer(c_int8_t), intent(inout) :: b(:) integer, intent(in) :: w, h, stride integer :: y, x, idx do y = 0, min(w-1, h-1) x = y idx = y*stride + x*4 + 1 if (idx+3 <= size(b)) then b(idx ) = 255_c_int8_t b(idx+1) = 255_c_int8_t b(idx+2) = 255_c_int8_t end if end do end subroutine end program


