fortran66のブログ

fortran について書きます。

【メモ帳】gif 画像出力 

gif 画像出力 module 分割

parameterized derived type は使わないことにした。よくよく考えると、元々 len 変数にサイズが依存する要素が無かったDeath! 

元々 parameterized derived type は、1987年以前の Fortran 8X で提案されていた機能で、オブジェクト指向とは別の発想で同様な機能拡張を実現しようとしたものだと思われます。その頃から20年 Fortran 2003 でオブジェクト指向の要素が取り入れられた後では、配列長 len に関してはコンストラクタを定義しての allocatation が使え、種別 kind に関しては相応のクラス定義ないし万能クラス class(*) で対応できそうな気がします。

parameterized derived type は、プログラムも面倒な上に予想しにくい挙動もあり、gfortran ではまだ実装されていないようだし、Intelコンパイラでもバグがなかなか取れないので、無理して使う必要もないかと思うこの頃。引数を持った型定義は、関数型言語などの影響か、流行っている気もしますが、Fortran では制限もあって使いにくいです。

出力結果

f:id:fortran66:20180503221621g:plain

  • 八色タイル

f:id:fortran66:20180503221626g:plain

Modern Fortran Explained (Numerical Mathematics and Scientific Computation)

Modern Fortran Explained (Numerical Mathematics and Scientific Computation)

  • 作者: Michael Metcalf,John Reid,Malcolm Cohen
  • 出版社/メーカー: Oxford University Press, U.S.A.
  • 発売日: 2011/05/08
  • メディア: ペーパーバック
  • この商品を含むブログを見る

ソース・プログラム

  • 派生型の定義  module m_gif_types

ユーザー定義型 I/O を使った。
function と interface でコンストラクタを定義して初期値を与えた。 

  • LZW 圧縮エンコーダ module m_gif_lzw

モジュール内での共有変数を使った。
アルゴリズムを簡素化。

  • gif ファイル書き出し module m_gif

将来アニメーション gif が出せるように image ブロック独立化の含みを少し持たせた。
アルゴリズムを簡素化。

    module m_gif_types
      use, intrinsic :: iso_fortran_env
      implicit none    
      interface t_gif_header
        procedure :: init_gif_header
      end interface 

      interface t_image_block
        procedure :: init_image_block
      end interface 

      type :: t_gif_header
        character(3)   :: signature = 'GIF'
        character(3)   :: version   = '89a' ! '87a'
        integer(int16) :: width  
        integer(int16) :: height 
        integer(int8)  :: pck = int(B'10001010', int8) ! 1:Global Col. 3:Color Res. 1:Sort Flg. 3:Size of Global Col.
        integer(int8)  :: background_color_index = 0
        integer(int8)  :: pixel_aspect_ratio     = 0
      contains
        procedure :: wr_gif_header
        generic   :: write(unformatted) => wr_gif_header
      end type  
       
      type :: t_image_block
        integer(int8)  :: image_separator = Z'2C'
        integer(int16) :: image_left_position = 00
        integer(int16) :: image_top_position  = 00
        integer(int16) :: image_width   
        integer(int16) :: image_height 
        integer(int8)  :: pck = int(B'00000000', int8)
        ! local color table
        !integer(int8) :: LZW_minimum_code_size 
        !integer(int8) :: block_size  
        !integer(int8) :: image_data
        !integer(int8) :: block_terminator = Z'00'
      contains
        procedure :: wr_image_block
        generic   :: write(unformatted) => wr_image_block
      end type t_image_block
      !
      private
      public :: t_gif_header, t_image_block
    contains
      type(t_gif_header) function init_gif_header(nx, ny, ngcol) result(res)
        integer, intent(in) :: nx, ny, ngcol
        res%width  = nx
        res%height = ny
        res%pck = iand(res%pck, B'11111000') + int(mod(ngcol, 8), int8) ! least 3bits : size of global color : 2**(ngcol + 1) 2..256
      end function init_gif_header
    
      type(t_image_block) function init_image_block(nx, ny) result(res)
        integer      , intent(in) :: nx, ny
        res%image_width  = nx
        res%image_height = ny
      end function init_image_block
    
      subroutine wr_gif_header(this, iw, iostat, iomsg)
        class(t_gif_header), intent(in) :: this
        integer, intent(in ) :: iw
        integer, intent(out) :: iostat
        character(*), intent(in out) :: iomsg
        write(iw) this%signature 
        write(iw) this%version   
        write(iw) this%width 
        write(iw) this%height
        write(iw) this%pck
        write(iw) this%background_color_index
        write(iw) this%pixel_aspect_ratio
        iostat = 0
      end subroutine wr_gif_header

      subroutine wr_image_block(this, iw, iostat, iomsg)
        class(t_image_block), intent(in) :: this
        integer, intent(in ) :: iw
        integer, intent(out) :: iostat
        character(*), intent(in out) :: iomsg
        write(iw) this%image_separator
        write(iw) this%image_left_position  
        write(iw) this%image_top_position 
        write(iw) this%image_width 
        write(iw) this%image_height 
        write(iw) this%pck 
        iostat = 0
      end subroutine wr_image_block
    end module m_gif_types 
    
    module m_gif_lzw
      use, intrinsic :: iso_fortran_env
      use m_gif_types
      implicit none
      type :: t_enc
        integer :: kbits = 0, id = 0
      end type    
      ! module variables
      integer :: dict(0:2**12 - 1), kd                ! 0-based,  used 0..kd
      integer :: nbits, kbits                         ! LZW minimum bit / LZW bit length
      !
      private
      public :: encoder
    contains
      subroutine clear_dict()
        integer :: i
        nbits = nbits
        kbits = nbits + 1
        kd    = 2**nbits + 1                          ! dictinary is used to kd
        dict  = 0
        forall(i = 0:kd) dict(i) = i                  ! dict(nbit) clear_code, dict(nbit+1) end_code 
      end subroutine clear_dict
    
      subroutine subenc(ienc, m1, m0)
        integer, intent(   out) :: ienc
        integer, intent(in    ) :: m1
        integer, intent(in out) :: m0
        integer :: k, id
        k = ishft(m0 + 1, 16) + m1                    ! ibuffer + 1 to avoid 0, 0 degeneracy 
        id = findloc(dict, k, dim = 1)                ! dictionary is 0-based 
        if (id == 0) then                             ! not found in the dictionary
          kd = kd + 1
          dict(kd) = k
          if (kd == 2**kbits + 1) kbits = kbits + 1
          ienc = m0
          m0   = m1
        else                                          ! found in the dictionary
          ienc = -1
          m0   = id - 1                               ! dictionary is 0-based
        end if    
      end subroutine subenc
    
      subroutine enc(irgb, code)
        integer, intent(in) :: irgb(:)
        type(t_enc), intent(out) :: code(:)
        integer :: i, ip, ic, ienc, ibuffer
        call clear_dict()                             ! clear dictionary and kd 
        ic = 1
        code(ic) = t_enc(kbits, 2**nbits)             ! clear_code 
        ibuffer = irgb(1)                             ! first pixel 
        do ip = 2, size(irgb)
          call subenc(ienc, irgb(ip), ibuffer) 
          if (ienc /= -1) then                        ! not found in the dictionary
            ic = ic + 1
            code(ic) = t_enc(kbits, ienc)
          end if  
          if (kd == 2**12 - 1) then                   ! dictionary full
            ic = ic + 1   
            code(ic) = t_enc(kbits, 2**nbits)         ! clear_code
            call clear_dict()                         ! clear dictionary 
          end if
        end do
        ic = ic + 1
        code(ic) = t_enc(kbits, ibuffer)              ! last pixel in buffer  
        ic = ic + 1 
        code(ic) = t_enc(kbits, 2**nbits + 1)         ! end_code  
      end subroutine enc
      
      subroutine encoder(nbs, irgb, icode)
        integer, intent(in ) :: nbs, irgb(:)
        integer(int8), allocatable, intent(out) :: icode(:)
        type(t_enc), allocatable :: code(:)
        integer :: i, j, k, ib, ic, nb
        nbits = nbs                                   ! nbits is a module variable   
        allocate(code(size(irgb) + 100))              ! ? large enough ?
        call enc(irgb, code)                          ! compress rgb to LZW code
        nb = ceiling(sum(code(:)%kbits) / 8.0)        !   required bytes  
        allocate(icode(nb))
        icode = 0                                     ! pack code to icode (bit string)    
        k = 0
        do i = 1, size(code)
          do j = 1, code(i)%kbits   
            if (btest(code(i)%id, j - 1)) then 
              ic = k / 8 + 1 
              ib = mod(k, 8)
              icode(ic) = ibset(icode(ic), ib)
            end if
            k = k + 1
          end do  
        end do
      end subroutine encoder 
    end module m_gif_lzw  
      
    module m_gif
      use, intrinsic :: iso_fortran_env
      use m_gif_types
      use m_gif_lzw
      implicit none
      private
      public :: wr_gif
    contains
      subroutine wr_gif(fn, irgb2)
        character(*), intent(in) :: fn
        integer     , intent(in) :: irgb2(:, :)
        type(t_gif_header ), allocatable :: head
        type(t_image_block), allocatable :: img    
        integer :: n_global_color 
        integer(int8), allocatable :: global_color_table(:, :)
        integer(int8), allocatable :: icode(:)
        integer :: nx, ny, nbits 
        ! set global color table
        n_global_color = 2
        allocate(global_color_table(3, 2**(n_global_color + 1)))
        global_color_table = 0
        global_color_table = reshape([[  0,  0,  0], [255,  0,  0], [0,255,  0], [  0,  0,255], &
                                      [255,255,  0], [255,  0,255], [0,255,255], [255,255,255]], [8,3])
        !  
        nx = size(irgb2, 1)
        ny = size(irgb2, 2)
        head = t_gif_header(nx, ny, n_global_color)  
        img  = t_image_block(nx, ny) 
        nbits = max(3, n_global_color + 1)           
        !
        block 
          integer :: ix, iy
          integer, allocatable :: irgb(:)
          allocate(irgb(size(irgb2)))
          forall (ix = 1:nx, iy = 1:ny) irgb(ix + nx * (iy - 1)) = irgb2(ix, iy)
          call encoder(nbits, irgb, icode)             ! rgb color to GIF LZW code
        end block  
        !
        block
          integer :: iw, i, nbyte
          open(newunit = iw, file = fn, access = 'stream')
          write(iw) head      
          write(iw) global_color_table
          write(iw) img     
          write(iw) achar(nbits)                     ! LZW_minimum_code_size = 2   (2+1=3bit)
          do i = 1, size(icode), 255         
            nbyte = min(i + 255, size(icode)) - i
            write(iw) achar(nbyte)             
            write(iw) icode(i:i+nbyte-1)
          end do
          write(iw) achar(00)                          ! block_terminator
          write(iw) achar(Z'3B')                     ! end of gif file
          close(iw)
        end block
      end subroutine wr_gif  
    end module m_gif
    
    program test
      use m_gif_types
      use m_gif
      implicit none
      integer, parameter :: nx = 512, ny = 512
      integer :: irgb(nx, ny)
      !
      block                       ! sin cos curve
        real :: x, y(5), pi = 4*atan(1.0)
        integer :: i
        do i = 1, 512
          x = 4 * pi / nx * i
          y = 255 * [sin(x), cos(x), sin(x / 2), cos(x / 2), 0.0] + 256
          irgb(i, int(y)) = [1, 2, 4, 6, 7]
        end do  
      end block
      call wr_gif('sincos.gif', irgb)
!      
      block                        ! tile  
        integer :: ix, iy
        forall (ix = 1:nx, iy = 1:ny) irgb(ix, iy) = mod(ix / 40 + iy / 40, 8) 
      end block
      call wr_gif('tile.gif', irgb)
    end program test

(H30.5.8 バグ修正)