Съвет за Java 60: Запазване на растерни файлове в Java

Този съвет допълва Java Tip 43, който демонстрира процеса на зареждане на растерни файлове в Java приложения. Този месец проследявам урок за това как да запазвам изображения в 24-битови растерни файлове и фрагмент на код, който можете да използвате, за да напишете растерни файлове от обект на изображение.

Възможността за създаване на растерни файлове отваря много врати, ако работите в среда на Microsoft Windows. Например при последния ми проект трябваше да свържа Java с Microsoft Access. Програмата Java позволява на потребителя да рисува карта на екрана. След това картата беше отпечатана в отчет на Microsoft Access. Тъй като Java не поддържа OLE, единственото ми решение беше да създам растерни файлове на картата и да кажа на отчета на Microsoft Access къде да го взема. Ако някога е трябвало да напишете приложение, за да изпратите изображение в клипборда, този съвет може да ви бъде от полза - особено ако тази информация се предава на друго приложение на Windows.

Форматът на растерния файл

Форматът на растерните файлове поддържа 4-битово RLE (кодиране с дължина на изпълнение), както и 8-битово и 24-битово кодиране. Тъй като имаме работа само с 24-битовия формат, нека разгледаме структурата на файла.

Растерният файл е разделен на три раздела. Изложих ви ги по-долу.

Раздел 1: Заглавка на растерния файл

Този заглавие съдържа информация за размера на типа и оформлението на растерния файл. Структурата е както следва (взето от дефиниция на структурата на езика C):

typedef struct tagBITMAPFILEHEADER {UINT bfType; DWORD bfSize; UINT bfReserved1; UINT bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;

Ето описание на кодовите елементи от горния списък:

  • bfType: Показва типа на файла и винаги е зададен на BM.
  • bfSize: Указва размера на целия файл в байтове.
  • bfReserved1: Резервирано - трябва да бъде зададено на 0.
  • bfReserved2: Резервирано - трябва да бъде зададено на 0.
  • bfOffBits: Определя изместването на байта от BitmapFileHeaderдо началото на изображението.

Тук видяхте, че целта на заглавката на растерното изображение е да идентифицира растерния файл. Всяка програма, която чете растерни файлове, използва заглавката на растерното изображение за проверка на файла.

Раздел 2: Заглавие на информацията за растерни изображения

Следващият хедър, наречен информационен, съдържа всички свойства на самото изображение.

Ето как посочвате информация за измерението и цветовия формат на растерно изображение (DIB) на устройство с Windows 3.0 (или по-нова версия):

typedef struct tagBITMAPINFOHEADER {DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD бикомпресия; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrВажно; } BITMAPINFOHEADER;

Всеки елемент от горния списък с кодове е описан по-долу:

  • biSize: Указва броя на байтовете, изисквани от BITMAPINFOHEADERструктурата.
  • biWidth: Указва ширината на растерното изображение в пиксели.
  • biHeight: Указва височината на растерното изображение в пиксели.
  • biPlanes: Указва броя равнини за целевото устройство. Този член трябва да бъде настроен на 1.
  • biBitCount: Указва броя на битовете на пиксел. Тази стойност трябва да бъде 1, 4, 8 или 24.
  • biCompression: Указва типа на компресия за компресирано растерно изображение. В 24-битов формат променливата е зададена на 0.
  • biSizeImage: Указва размера в байтове на изображението. Валидно е да зададете този член на 0, ако растерното изображение е във BI_RGBформата.
  • biXPelsPerMeter: Указва хоризонталната разделителна способност, в пиксели на метър, на целевото устройство за растерното изображение. Приложението може да използва тази стойност, за да избере растерно изображение от група ресурси, което най-добре отговаря на характеристиките на текущото устройство.
  • biYPelsPerMeter: Указва вертикалната разделителна способност, в пиксели на метър, на целевото устройство за растерното изображение.
  • biClrUsed: Задава броя на цветните индекси в таблицата с цветове, действително използвани от растерното изображение. Ако biBitCountе зададено на 24, biClrUsedуказва размера на референтната цветова таблица, използвана за оптимизиране на производителността на цветните палитри на Windows.
  • biClrImportant: Указва броя на цветните индекси, считани за важни за показване на растерното изображение. Ако тази стойност е 0, всички цветове са важни.

Сега цялата информация, необходима за създаване на изображението, е дефинирана.

Раздел 3: Изображение

В 24-битовия формат всеки пиксел в изображението е представен от поредица от три байта RGB, съхранени като BRG. Всяка линия за сканиране е подплатена до равномерна 4-байтова граница. За да усложни още малко процеса, изображението се съхранява отдолу нагоре, което означава, че първата линия за сканиране е последната линия за сканиране в изображението. Следващата фигура показва заглавките ( BITMAPHEADER) и ( BITMAPINFOHEADER) и част от изображението. Всеки раздел е разграничен от вертикална лента:

0000000000 4D42 B536 0002 0000 0000 0036 0000 | 0028 0000000020 0000 0107 0000 00E0 0000 0001 0018 0000 0000000040 0000 B500 0002 0EC4 0000 0EC4 0000 0000 0000000060 0000 0000 0000 | FFFF FFFF FFFF FFFF FFFF 0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF *

Сега, на кода

Сега, след като знаем всичко за структурата на 24-битов растерния файл, ето какво чакате: кодът за запис на растерния файл от обект на изображение.

импортиране на java.awt. *; импортиране на java.io. *; импортиране на java.awt.image. *; публичен клас BMPFile разширява Component {// --- Частни константи private final static int BITMAPFILEHEADER_SIZE = 14; частен окончателен статичен int BITMAPINFOHEADER_SIZE = 40; // --- Частна декларация на променлива // --- Заглавка на битмап файл частен байт bitmapFileHeader [] = нов байт [14]; частен байт bfType [] = {'B', 'M'}; private int bfSize = 0; private int bfReserved1 = 0; private int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- Заглавка на информация за растерни изображения частен байт bitmapInfoHeader [] = нов байт [40]; private int biSize = BITMAPINFOHEADER_SIZE; private int biWidth = 0; private int biHeight = 0; private int biPlanes = 1; private int biBitCount = 24; private int biCompression = 0; private int biSizeImage = 0x030000;private int biXPelsPerMeter = 0x0; private int biYPelsPerMeter = 0x0; private int biClrUsed = 0; private int biClrImportant = 0; // --- Растерни данни за сурови данни int int bitmap []; // --- Файлова секция private FileOutputStream fo; // --- Конструктор по подразбиране public BMPFile () {} public void saveBitmap (String parFilename, Image parImage, int parWidth, int parHeight) {try {fo = new FileOutputStream (parFilename); запазване (parImage, parWidth, parHeight); fo.close (); } catch (Exception saveEx) {saveEx.printStackTrace (); }} / * * SaveMethod е основният метод на процеса. Този метод * ще извика метода convertImage, за да преобразува изображението от паметта в * байтов масив; метод writeBitmapFileHeader създава и записва * заглавката на растерния файл; writeBitmapInfoHeader създава заглавката * информация; и writeBitmap записва изображението.* * / private void save (Image parImage, int parWidth, int parHeight) {try {convertImage (parImage, parWidth, parHeight); writeBitmapFileHeader (); writeBitmapInfoHeader (); writeBitmap (); } catch (Exception saveEx) {saveEx.printStackTrace (); }} / * * convertImage преобразува изображението от паметта в растерния формат (BRG). * Той също така изчислява известна информация за заглавката на растерната информация. * * / private boolean convertImage (Image parImage, int parWidth, int parHeight) {int pad; растерна карта = нов int [parWidth * parHeight]; PixelGrabber pg = нов PixelGrabber (parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); опитайте {pg.grabPixels (); } catch (InterruptedException e) {e.printStackTrace (); връщане (невярно); } pad = (4 - ((parWidth * 3)% 4)) * parHeight; biSizeImage = ((parWidth * parHeight) * 3) + подложка;bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight; връщане (вярно); } / * * writeBitmap преобразува изображението, върнато от пикселния грайфер, в * необходимия формат. Запомнете: линиите за сканиране се обръщат в * растерния файл! * * Всяка линия за сканиране трябва да бъде подплатена до равномерна 4-байтова граница. * / private void writeBitmap () {int размер; стойност int; int j; int i; int rowCount; int rowIndex; int lastRowIndex; int подложка; int padCount; байт rgb [] = нов байт [3]; размер = (biWidth * biHeight) - 1; pad = 4 - ((biWidth * 3)% 4); if (pad == 4) // <==== Pad за коригиране на грешки = 0; // <==== Поправка на грешки rowCount = 1; padCount = 0; rowIndex = размер - biWidth; lastRowIndex = rowIndex; опитайте {for (j = 0; j> 8) & 0xFF); rgb [2] = (байт) ((стойност >> 16) & 0xFF); fo.write (rgb);if (rowCount == biWidth) {padCount + = pad; за (i = 1; i> 8) & 0x00FF); връщане (retValue); } / * * * intToDWord преобразува int в двойна дума, където връщаната * стойност се съхранява в 4-байтов масив. * * / частен байт [] intToDWord (int parValue) {байт retValue [] = нов байт [4]; retValue [0] = (байт) (parValue & 0x00FF); retValue [1] = (байт) ((parValue >> 8) & 0x000000FF); retValue [2] = (байт) ((parValue >> 16) & 0x000000FF); retValue [3] = (байт) ((parValue >> 24) & 0x000000FF); връщане (retValue); }}0x00FF); retValue [1] = (байт) ((parValue >> 8) & 0x000000FF); retValue [2] = (байт) ((parValue >> 16) & 0x000000FF); retValue [3] = (байт) ((parValue >> 24) & 0x000000FF); връщане (retValue); }}0x00FF); retValue [1] = (байт) ((parValue >> 8) & 0x000000FF); retValue [2] = (байт) ((parValue >> 16) & 0x000000FF); retValue [3] = (байт) ((parValue >> 24) & 0x000000FF); връщане (retValue); }}

Заключение

Това е всичко. Сигурен съм, че този клас ще ви бъде много полезен, тъй като от JDK 1.1.6 Java не поддържа запазване на изображения в нито един от популярните формати. JDK 1.2 ще предлага поддръжка за създаване на JPEG изображения, но не и поддръжка на растерни изображения. Така че този клас все още ще запълни празнина в JDK 1.2.

Ако си поиграете с този клас и намерите начини да го подобрите, кажете ми! Моят имейл се появява отдолу, заедно с биографията ми.

Жан-Пиер Дюбе е независим консултант по Java. Той основава Infocom, регистриран през 1988 г. Оттогава Infocom разработва няколко потребителски приложения, вариращи от производство, управление на документи и широкомащабно управление на електрически линии. Той има богат опит в програмирането на C, Visual Basic и наскоро Java, който сега е основният език, използван от неговата компания. Един от последните проекти на Infocom е API за диаграми, който скоро трябва да се появи като бета версия.

Тази история „Java Tip 60: Запазване на растерни файлове в Java“ първоначално е публикувана от JavaWorld.