Utilizando mecanismos de cache em RMS para imagens
Dados do artigo
Artigo
Quando você tem imagens grandes e complexas, que são desenhadas em tempo de execução no seu aplicativo usando a API de baixo nível (por exemplo, montanhas fractal como imagem de fundo ou o sol multicolorido com brilhos e manchas fuzzy), por vezes, leva muito tempo para redesenhar eles (principalmente nos primeiros dispositivos com java embarcado).
Claro, você pode preparar uma imagem nos formatos PNG ou JPG de antemão e utilizá-lo em vez de usar métodos de baixo nível. Mas se a sua aplicação deve funcionar em diferentes dispositivos móveis com diferentes resoluções de tela, então você é obrigado a criar um grande número de tais imagens em diferentes resoluções e, quando o algoritmo é alterado, você precisa voltar e convertê-los todos.
O caminho alternativo é utilizar mecanismos de cache quando as imagens são desenhadas em tempo de execução no dispositivo e depois utilizar a imagem armazenada no cache para redesenhar. Para isso, você deve usar os seguintes métodos:
- Image.createImage(width, height)
- image.getGraphics().
Depois de ter um objeto do tipo Image que contém uma imagem complexa, você pode guardá-lo na RMS para não necessitar desenhar novamente depois de executar seu aplicativo novamente.
Atenção:' você deve calcular quais opções serão mais rápidas: lendo do cache RMS de imagens ou executando os algoritmos complexos para desenahr a imagem
Neste artigo, vou apresentar-lhe um padrão para armazenamento de objetos de imagens em cache utilizando Record Management System (RMS) e, em seguida, restaurá-los após a execução da aplicação.
Abaixo segue o código-fonte da classe que irá salvar as imagens em Record Management System (RMS).
public class ImageStorage {
public static final int ID_MOUNTAIN = 0;//índices para as imagens que serão armazenadas em cache
public static final int ID_SUN = 1;
public static final int NUMBER_OF_IMAGES = ID_SUN + 1;
private static final Image[] CASHED_IMAGES = new Image[NUMBER_OF_IMAGES];//array de imagens criado
private static final int MAX_AREA_OF_IMAGE = 176 * 220;
// Se precisamos de mais, ela vai crescer em run-time
private static final String RMS_IMAGES = "images";
//nome de armazenamento em RMS
/**
* Esta cor será usada para indicar a cor transparente,
* Você pode alterá-la, mas tenha certeza de que
* Sua cor será processada corretamente pelo aplicativo.
* Ela é modificada apenas uma vez (você pode fazer isso final)
*/
protected static int COLOR_TO_BE_TRANSPARENT = 0xFFFFFF;
public static boolean isLoaded = false; //sinalizador indica que as imagens foram carregadas em RMS
/**
* Classe DrawerImageToBuffer para simplificar o cache de imagens em gráficos de buffer(o padrão é o "método modelo" GOF)
*/
public static abstract class DrawerImageToBuffer {
protected int width;
protected int height;
private Image imgBuffer = null;
private DrawerImageToBuffer() {
}
/**
* Salva imagem, que é produzida pelo método drawImage,
* no array CASHED_IMAGES
*
* @param w - captura a largura d aimagem
* @param h - captura a altura da imagem
* @param imageIndex - captura o índice da imagem
*/
protected void process(int w, int h, int imageIndex) {
width = w;
height = h;
imgBuffer = Image.createImage(width, height);
Graphics g = imgBuffer.getGraphics();
g.setColor(COLOR_TO_BE_TRANSPARENT);
g.fillRect(0, 0, imgBuffer.getWidth(), imgBuffer.getHeight());
drawImage(g);
saveImageToArray(imageIndex);
}
abstract protected void drawImage(Graphics g);//Método modelo
/**
* @param imageIndex - captura o índice da imagem
*/
private void saveImageToArray(int imageIndex) {
CASHED_IMAGES[imageIndex] = imgBuffer;
}
}
//------------------------------------------------
/**
* O método PaintToBufferAndStoreImages() passa por todas as imagens que devem ser armazenados em cache,
* Invocando seus algoritmos complicados de pintura, armazenando resultados da pintura em ImageStorage.CASHED_IMAGES,
* E, em seguida, salva as imagens em RMS
*/
protected void paintToBufferAndStoreImages() {
DrawerImageToBuffer TM = null;
int size = Font.getDefaultFont().getHeight();
TM = new MountainDrawingToCache();
TM.process(size, size, ID_MOUNTAIN);
TM = new SunDrawingToCache();
TM.process(4, Font.getDefaultFont().getHeight() * 8, ID_SUN);
storeImagesInRMS(CASHED_IMAGES);//salvar imagens em RMS
}
/**
* Obtém tabela de cores de curImage imagem em rgbInts array de inteiros
*
* @param rgbInts - matriz resultado com a tabela de cores da imagem
* @param curImage - objeto de imagem a partir do qual temos a tabela de cores
* @param w - largura da imagem obtida (que pode levar toda a região não a imagem)
* @param h - altura da imagem obtida (que pode levar toda a região não a imagem)
* @param s - área da imagem (deve ser w * h)
*/
protected static void getRGB(int[] rgbInts, Image curImage, int w, int h, int s) {
curImage.getRGB(rgbInts, 0, w, 0, 0, w, h);
for (int i = 0; i < s; i++)
if ((rgbInts[i] & 0x00FFFFFF) == COLOR_TO_BE_TRANSPARENT)
rgbInts[i] = (rgbInts[i] & 0x00FFFFFF);
}
/**
* Verifica se é preciso atualizar o cache
* Você pode usar dados que são armazenados em RMS se achar que são necessários para atualização
*
* @param recordStore - RMS com dados adicionais
* @return false se dados em RMS é up-to-date
* Caso contrário retorna true
* @throws RecordStoreException
* @throws IOException
*/
protected boolean additioanlCheckingOfNeedingReCashing(RecordStore recordStore) throws RecordStoreException, IOException {
/* verificação adicional por exemplo, dados em RMS estão fora de moda por causa da fonte alterada ou resolução */
return false;
}
/**
* Restaura uma série de imagens de RMS para array de imagens creditadas
*
* @param arImages - conjunto de imagens a serem restauradas
*/
protected void restoreImagesFromRMS(Image[] arImages) {
int[] intArrayOfRGBforImage = null;
int w = 0;//largura da imagem
int h = 0;//altura da imagem
int l = 0;//área da imagem
int curPointerToImage = 0;
try {
RecordStore recordStore = RecordStore.openRecordStore(RMS_IMAGES, true);
RecordEnumeration re = recordStore.enumerateRecords(null, null, true);
/* Aqui você pode colocar o código para a tomada de informações adicionais para o re-cashing em RMS (você deve simplesmente ignorá-lo)
* Porque ele já é processado) */
/*...*/
try {
while (re.hasNextElement()) {
int id = re.nextRecordId();
ByteArrayInputStream bais = new ByteArrayInputStream(recordStore.getRecord(id));
DataInputStream inputStream = new DataInputStream(bais);
try {
l = inputStream.readInt();
w = inputStream.readInt();
h = inputStream.readInt();
intArrayOfRGBforImage = new int[l];
for (int j = 0; j < l; j++)
intArrayOfRGBforImage[j] = inputStream.readInt();
} catch (EOFException ioe) {
ioe.printStackTrace();
}
arImages[curPointerToImage++] = Image.createRGBImage(intArrayOfRGBforImage, w, h, true);
System.gc();
}
}
catch (IOException ioe) {
ioe.printStackTrace();
}
recordStore.closeRecordStore();
} catch (Exception rse) {
rse.printStackTrace();
}
}
/**
* Arrays de imagens em RMS do conjunto de imagens em chache
*
* @param images - conjunto de imagens a serem armazenadas
*/
public void storeImagesInRMS(Image[] images) {
int w, h, l;
int[] rgbImage = new int[MAX_AREA_OF_IMAGE];
try {
try {// apaga os registros armazenados
RecordStore.deleteRecordStore(RMS_IMAGES);
} catch (RecordStoreException e) {
e.printStackTrace();
}
RecordStore recordStore = RecordStore.openRecordStore(RMS_IMAGES, true);
/*Salvar informações adicionais para verificação a necessidade de re-cashing em RMS */
/*...*/
for (int i2 = 0; (i2 < images.length) && (images[i2] != null); i2++) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream outputStream = new DataOutputStream(baos);
Image curImage = images[i2];
w = curImage.getWidth();
h = curImage.getHeight();
l = w * h;
if (l > MAX_AREA_OF_IMAGE)
rgbImage = new int[l];
getRGB(rgbImage, curImage, w, h, l);
try {
outputStream.writeInt(l);
outputStream.writeInt(w);
outputStream.writeInt(h);
for (int j = 0; j < l; j++)
outputStream.writeInt(rgbImage[j]);
System.gc();
} catch (IOException ioe) {
ioe.printStackTrace();
}
byte[] b = baos.toByteArray();
int id = recordStore.addRecord(b, 0, b.length);
}
recordStore.closeRecordStore();
} catch (Exception rse) {
rse.printStackTrace();
}
}
/**
* Verifica se os dados em RMS (que contém imagens em cache) é up-to-date e se for em seguida
* Carrega do cache as imagens em RMS. Se não for, então a aplicação do cache das imagens usando
* o método DrawImage adequado, guarda em RMS, e em seguida, carrega o cache das imagens em RMS.
*
* @throws Exception
*/
public void loadImages() throws Exception {
RecordStore recordStore = RecordStore.openRecordStore(RMS_IMAGES, true);
if (recordStore.getNumRecords() == 0)
paintToBufferAndStoreImages();
else {
boolean isNeedReCash = additioanlCheckingOfNeedingReCashing(recordStore);
if (isNeedReCash)
paintToBufferAndStoreImages();
}
restoreImagesFromRMS(CASHED_IMAGES);
isLoaded = true;
}
/**
* Use este método em vez de Graphics.DrawImage para desenhar imagens complicadas no cache
*
* @param g - Objeto da Classe {@link javax.microedition.lcdui.Graphics}
* @param index - índice de imagem armazenadas em um array de imagens cache
* @param x - x coordenada para desenho x
* @param y - y coordenada para desenho y
* @param anchor - âncora da imagem pintada
*/
public static void drawImage(Graphics g, int index, int x, int y, int anchor) {
if (ImageStorage.isLoaded)
try {
g.drawImage(CASHED_IMAGES[index], x, y, anchor);
} catch (Exception e) {
isLoaded = false;
e.printStackTrace();
}
}
}
Nesta classe Mountain você deve definir os métodos drawSingleMountain que irá conter todo código de pintura da montanha em objetos Graphics. Também deverás fazer na classe Sun no método drawSingleSun.
// Os métodos sobrescritos (você pode colocar esta aula em uma classe adequada (Mountain e Sun))
public static class MountainDrawingToCache extends DrawerImageToBuffer {
public void drawImage(Graphics g) {
Mountain.drawSingleMountain(g/*plus other parametres if needed*/);
}
}
public static class SunDrawingToCache extends DrawerImageToBuffer {
public void drawImage(Graphics g) {
Sun.drawSingleSun(g/*plus other parametres if needed*/);
}
}
No método startApp da sua MIDlet, você pode definir o seguinte trecho de código:
protected void startApp() throws MIDletStateChangeException {
ImageStorage ids = new ImageStorage();
try {
ids.loadImages();
} catch (Exception e) {
ids.isLoaded = false;
ids.paintToBufferAndStoreImages();
}
/* outro código... */
}
Agora, a fim de desenhar as imagens armazenadas, você deve utilizar o método drawImage(Graphics g, int index, int x, int y, int anchor) da classe ImageStorage.


(no comments yet)