BMP writer
Autor: zecomeia | Publicado: 2010-11-01T18:08:00-02:00

BMP writerpor zecomeia
Introdução

Método implementado na classe IO para escrever um arquivo de imagem do tipo bmp truecolor de 24bits a partir de um objeto da classe Bitmap usada no RGSS.

Características

bmp truecolor 24bits
resolução vertical e horizontal de 96dpi
utiliza um objeto da classe Bitmap do RGSS para gerar o arquivo de imagem

Como Usar

*Ao abrir o arquivo para a escrita, use o modo BINÁRIO.
file = File.new('image.bmp','wb')

Para gravar a imagem no arquivo, use o metodo bmp_write após abrir o arquivo.
file.bmp_write(aBitmap_obj)

Feche o arquivo.
file.close

OBS: A escrita é um pouco demorada, dependendo tamanho da imagem pode levar alguns segundos. Veja a técnica de uso do script a seguir para resolver o problema.


Usando o BMP writer num processor paralelo 

O script não utliza bibliotecas externas. Foi escrito totalmente em Ruby baseando-se no objeto Bitmap usado em RGSS/2. Mesmo sendo feito para ter um bom desempenho com relação ao tempo de escrita de arquivo, para empregá-lo em jogos o tempo de escrita pode acabar sendo longo. Por exemplo, em uma máquina com Core 2 Duo o script levou 2 segundos para gravar um bitmap de 640x480, o que parece rápidos, mas pode ser um incmodo no meio de um jogo.

Então resolvi usar o metodo de escrita em um processo paralelo para não ocorrer aquela pausa no jogo enquanto a gravação do arquivo está em andamento. O problema é que leva mais tempo para o arquivo ser salvo(seguindo mesmo exemplo anterior, levou 1 minuto e meio para a gravação), isso significa que não é possível visualizar/utilizar o arquivo gerao enquanto não terminar essa gravação, e também caso o jogo seja fechado antes do término o arquivo fica corrompido.

Para criar nosso processo paralelo usaremos uma Thread. Basicamente uma thread é um bloco de código que é executado em paralelo com o programa principal. A técnica não é muito complicada nem ocupa muito espaço. Primeiro vamos carregar um Bitmap para usar na gravação:

data_system        = load_data("Data/System.rxdata")
aBitmap_obj = RPG::Cache.title(data_system.title_name)

As linhas acima irão carregar a imagem de título na variável aBitmap_obj.

Agora vamos à Thread:

thread_bmp = Thread.new(aBitmap_obj) do
begin
file = File.new('image.bmp','wb')
file.bmp_write(aBitmap_obj)
file.close
rescue
print 'Erro ao gravar um bitmap'
end
end

a linha:

thread_bmp = Thread.new(aBitmap_obj) do

Cria nossa Thread. Observe que já passamos como argumentoda Thread o Bitmap que criamos antes. O "do" define o nosso bloco de código que termina no último "end".

O "begin" é um outro bloco que criei dentro da threadsó para poder usar o tratamentos de exceções(erros) da clausula "rescue". Isso porque quando ocorre um erro dentro da Thread o Game.exe fecha sem nem um aviso. Então se acontecer algum erro na Thread o que está entre o recue e o end será executado. No exemplo só cooquei uma mensagem, o que não é muito correto, porque se aconteceu algum erro a thread para mas o programa continua sem tratar do problema.

Entre o begin e o rescue deve entrar aquelas três linhas que já usavamos para gravar o arquivo antes.

file = File.new('image.bmp','wb')
file.bmp_write(aBitmap_obj)
file.close

Abre o arquivo para leitura, escreve o BMP no arquivo e fecha o arquivo quando terminar a escrita.

Basicamente é isso. Teste a técnica e qualquer dúvida fique a vontade.

Script


#========================================
# BMP writer
#----------------------------------------
#by:      zecomeia
#date:    01/11/2010
#for:     RGSS/RGSS2
#version: 1.0
#www.colmeia-do-ze.blogspot.com
#----------------------------------------
=begin
IO's method to write a BMP truecolor(24bits) file 
from a RGSS's Bitmap object.

-bmp truecolor 24bits
-resolution vertical and horizontal = 96dpi
-use a RGSS's Bitmap object to write the image file

*To open file to write, use BINARY mode.
file = File.new('image.bmp','wb')
To write use the bmp_write method after open file
file.bmp_write(aBitmap_obj)
Close file
file.close
=end

class IO
  
  def bmp_write(bitmap)
    self.write_FileHeader(bitmap)
    self.write_ImageHeader(bitmap)
    self.write_Bitmap(bitmap)    
    self.putc(0x00)
    self.putc(0x00)
  end
  
  def write_FileHeader(bitmap)    
    #sign
    self.putc(0x42)
    self.putc(0x4D)
    #file size
    w = bitmap.width
    h = bitmap.height
    img_size = w * h * 3 + 56 # 3bytes/pixel(true color 24bits)
    hexa = sprintf("%x",img_size)
    length = hexa.size
    hexa_byte = []    
    if length%2 != 0
    hexa = '0' + hexa
    length +=1
    end
    pointer_byte = 0
    for i in 0...length/2
      pointer_byte += 1
      hexa_byte[i] = hexa.unpack('a2')
      hexa.sub!(hexa_byte[i].to_s,'')
    end 
    for byte in hexa_byte.reverse
      self.putc(sh_i(byte.to_s))
    end
    (4 - pointer_byte).times { self.putc(0x00) }
    #reserved area
    4.times { self.putc(0x00) }
    #offset bits
    self.putc(0x36)
    3.times { self.putc(0x00) }
  end
  
  def write_ImageHeader(bitmap)
    #image header size
    self.putc(0x28)
    3.times { self.putc(0x00) }
    #width
    w = bitmap.width
    hexa = sprintf("%x",w)
    length = hexa.size
    hexa_byte = []
    if length%2 != 0
    hexa = '0' + hexa
    length +=1
    end
    pointer_byte = 0
    for i in 0...length/2
      pointer_byte += 1
      hexa_byte[i] = hexa.unpack('a2')
      hexa.sub!(hexa_byte[i].to_s,'')
    end 
    for byte in hexa_byte.reverse
      self.putc(sh_i(byte.to_s))
    end
    (4 - pointer_byte).times { self.putc(0x00) }
    #height
    h = bitmap.height
    hexa = sprintf("%x",h)
    length = hexa.size
    hexa_byte = []
    if length%2 != 0
    hexa = '0' + hexa
    length +=1
    end
    pointer_byte = 0
    for i in 0...length/2
      pointer_byte += 1
      hexa_byte[i] = hexa.unpack('a2')
      hexa.sub!(hexa_byte[i].to_s,'')
    end 
    for byte in hexa_byte.reverse
      self.putc(sh_i(byte.to_s))
    end
    (4 - pointer_byte).times { self.putc(0x00) }
    #planes
    self.putc(0x01)
    self.putc(0x00)
    #bits per pixel
    self.putc(0x18) # 3bytes/pixel(true color 24bits)
    self.putc(0x00)
    #compression
    4.times { self.putc(0x00) }
    #image size
    w = bitmap.width
    h = bitmap.height
    img_size = w*h*4
    hexa = sprintf("%x",img_size)
    length = hexa.size
    hexa_byte = []
    if length%2 != 0
    hexa = '0' + hexa
    length +=1
    end
    pointer_byte = 0
    for i in 0...length/2
      pointer_byte += 1
      hexa_byte[i] = hexa.unpack('a2')
      hexa.sub!(hexa_byte[i].to_s,'')
    end 
    for byte in hexa_byte.reverse
      self.putc(sh_i(byte.to_s))
    end
    (4 - pointer_byte).times { self.putc(0x00) }
    #XPP meter
    #(0ED8)HEX = (3800)DEC  = 96dpi
    self.putc(0xD8)
    self.putc(0x0E)
    self.putc(0x00)
    self.putc(0x00)
    #YPP meter
    #(0ED8)HEX = (3800)DEC  = 96dpi
    self.putc(0xD8)
    self.putc(0x0E)
    self.putc(0x00)
    self.putc(0x00)
    #number of colors used
    4.times { self.putc(0x00) }
    #number of important colors
    4.times { self.putc(0x00) }
  end
  
  def write_Squad
    
  end
  
  def write_Bitmap(bitmap)
    for y in 1..bitmap.height
      for x in 0...bitmap.width
        color = bitmap.get_pixel(x, bitmap.height - y)
        self.putc(color.blue)
        self.putc(color.green)        
        self.putc(color.red)
        #complete line with random bytes if necessary
        if (r = bitmap.width % 4) != 0 and x == (bitmap.width - 1)
          (4 - r).times { 3.times { self.putc(0xFF) } }
        end
      end
    end
  end
  
end

module Kernel
  
  #Convert a string with hex number to a integer
  #sh_i("FF") returns 255
  def sh_i(sh)
    eval("0x#{sh}")
  end
  
end


Créditos e Agradecimentos
Feito por zecomeia