GDI+ и многокадровые изображения в формате TIFFВ отличие от других растровых графических форматов, формат TIFF (Tag Image File Format) позволяет:
Формат обладает исключительной гибкостью. Вы можете выбрать свой алгоритм сжатия и количество бит на пиксель для каждого кадра, сохранённого в TIFF файле. В этой статье рассматриваются все аспекты работы с форматом TIFF из приложений Visual FoxPro. Преобразование графических форматов в GDI+GDI+ позволяет достаточно просто преобразовывать изображения из одного формата в другой; для этого функции сохранения изображения (например, GdipSaveImageToFile) достаточно просто передать ClsID соответствующего кодека. В следующем примере выполняется преобразование изображения из формата BMP в формат TIFF. DECLARE Long GdipLoadImageFromFile IN Gdiplus.dll String cFile, Long @ nativeImage DECLARE Long GdipSaveImageToFile IN Gdiplus.dll Long nativeImage, String cFile, ; String EncoderClsID, String EncoderParameters DECLARE Long GdipDisposeImage IN Gdiplus.dll Long nativeImage lcInputFile = STRCONV("InputFile.bmp" + CHR(0), 5) lcOutputFile = STRCONV("OutputFile.tif" + CHR(0), 5) lnImage = 0 GdipLoadImageFromFile(lcInputFile, @lnImage) lqEncoderClsID_TIFF = 0h05F47C55041AD3119A730000F81EF32E GdipSaveImageToFile(lnImage, lcOutFile, lqEncoderClsID_TIFF, NULL) GdipDisposeImage(lnImage) Функция GdipLoadImageFromFile считывает исходное изображение из файла InputFile.bmp в память компьютера, а функция GdipSaveImageToFile сохраняет это изображение в формате TIFF в файле OutputFile.tif. Эти функции требуют, чтобы имена файлов передавались им как нуль-терминированные строки в формате Unicode. Значения ClsID кодеков GDI+ перечислены в следующей таблице:
Microsoft предупреждает, что значения ClsID кодеков могут отличаться в различных версиях библиотеки GDI+. Поэтому более правильным будет использование специального механизма, предоставляемого функциями GdipGetImageEncodersSize и GdipGetImageEncoders, позволяющего получить значение ClsID кодека для заданного графического формата изображения. Скорее всего, такая необходимость появится в Windows Vista. Структура EncoderParametersВ рассмотренном выше примере при вызове функции GdipSaveImageToFile ей не передаётся указатель на структуру EncoderParameters (это последний, четвёртый параметр функции); вместо него указан NULL. Действительно, вы можете конвертировать любой поддерживаемый в GDI+ графический формат в другой и без использования этой структуры. Но, если вы хотите управлять качеством сохраняемого изображения формата JPEG или если вы создаёте многокадровые TIFF файлы или TIFF файлы, использующие алгоритмы сжатия и управления цветовой палитрой изображения, то тогда вы должны использовать структуру EncoderParameters для управления параметрами кодека. Фактически структура EncoderParameters представляет собой массив из вложенных структур. Первое поле этой структуры содержит четыре байта для хранения целочисленного значения, определяющего количество вложенных в неё структур. Каждая из вложенных структур содержит следующие поля:
Таким образом, поле Guid определяет, как именно кодек должен обработать передаваемый ему в структуре параметр. Значения поля Guid Для формата TIFF перечислены в следующей таблице.
Значения поля Type перечислены в следующей таблице:
При работе с форматом TIFF значение передаваемого кодеку параметра представляет собой одно 32-х разрядное целое число, поэтому значение поля NumberOfValues всегда должно быть равно единице, а значение поля Type — четырём (EncoderParameterValueTypeLong, 32-х разрядное целое без знака). Поле Value
содержит указатель на область памяти, в которой находится значение
параметра. Как следует из сказанного выше, размер этой памяти должен быть
равен 4-м байтам. В следующей таблице приведены допустимые значения параметра, передаваемого в структуре, управляющей созданием многокадрового TIFF (с полем Guid, содержащим EncoderSaveFlag):
Ниже показан фрагмент кода, демонстрирующий способ создания структуры EncoderParameters в Visual FoxPro. #DEFINE EncoderSaveFlag 0hFC66222940ACBF478CFCA85B89A655DE #DEFINE EncoderValueMultiFrame 18 hGlobal = GlobalAlloc(0x0040, 4) && Выделяем глобальную память = SYS(2600, hGlobal, 4, BINTOC(EncoderValueMultiFrame, "4RS") && Заносим в неё значение lcEncoderParams = BINTOC(1, "4RS") && 1 вложенная структура lcEncoderParams = lcEncoderParams + EncoderSaveFlag && Поле Guid lcEncoderParams = lcEncoderParams + BINTOC(1, "4RS") && Поле NumberOfValues lcEncoderParams = lcEncoderParams + BINTOC(4, "4RS") && Поле Type lcEncoderParams = lcEncoderParams + BINTOC(hGlobal, "4RS") && Поле Value Для хранения значения параметра в примере при помощи функции GlobalAlloc распределяется фиксированная глобальная память Windows. Запись многокадровых изображений в формате TIFFСоздание многокадрового изображения TIFF несколько отличается от записи обычного файла. Самый первый кадр, как обычно, записывается функцией GdipSaveImageToFile. Для добавления каждого следующего кадра используется функция GdipSaveAddImage, а функция GdipSaveAdd играет роль завершающего аккорда, добавляя в файл признак его окончания. Всем этим функциям передаётся указатель на структуру EncoderParameters, при этом передаваемое в структуре значение параметра (указатель на параметр хранится в поле Value) для каждой из этих функций будет различным:
В листинге 1 приведён код процедуры, создающей
многокадровый TIFF файл.
#DEFINE EncoderClsID_TIFF 0h05F47C55041AD3119A730000F81EF32E #DEFINE EncoderSaveFlag 0hFC66222940ACBF478CFCA85B89A655DE #DEFINE EncoderValueMultiFrame 18 #DEFINE EncoderValueFlush 20 #DEFINE EncoderValueFrameDimensionPage 23 LOCAL i, lnStatus, loDialogBox, lcInputFiles, lnCount, lcPath, lcOutputFile, lcFile LOCAL lnImage, lnNewImage, lcEncoderParameters, hGlobal, laFiles[1] * * Объявляем необходимые API функции * DECLARE Long GdipLoadImageFromFile IN Gdiplus.dll String, Long @ DECLARE Long GdipSaveImageToFile IN Gdiplus.dll Long, String, String, String DECLARE Long GdipSaveAddImage IN Gdiplus.dll Long, Long, String DECLARE Long GdipSaveAdd IN Gdiplus.dll Long, String DECLARE Long GdipDisposeImage IN Gdiplus.dll Long DECLARE Long GlobalAlloc IN WIN32API Long, Long DECLARE Long GlobalFree IN WIN32API Long * * Создаём объект Microsoft Common Dialog для множественного выбора файлов * loDialogBox = CREATEOBJECT('MSComDlg.CommonDialog') WITH loDialogBox .MaxFileSize = 16500 .Flags = 0x200 + 0x80000 .DialogTitle = "Выберите файлы для сохранения в формате TIFF" .Filter = "BMP|*.bmp|JPEG|*.jpg|GIF|*.gif|PNG|*.png|TIFF|*.tif|Все файлы (*.*)|*.*" .FilterIndex = 1 .ShowOpen lcInputFiles = .FileName ENDWITH loDialogBox = NULL IF EMPTY(lcInputFiles) RETURN ENDIF * * Разбор полученной строки с именами исходных файлов * lnCount = ALINES(laFiles, lcInputFiles, 1, CHR(0)) IF lnCount = 1 && В списке только один файл lcPath = "" && ... очищаем путь... ELSE lcPath = laFiles[1] + "\" && Первый элемент массива содержит путь, = ADEL(laFiles, 1) && а остальные - спецификации файлов lnCount = lnCount - 1 && ENDIF * * Запрашиваем спецификацию файла TIFF для сохранения исходных изображений * lcOutputFile = PUTFILE("Сохранить в", "result", "tif") IF EMPTY(lcOutputFile) RETURN ENDIF * * Считываем в память первый файл из списка * lcFile = STRCONV(lcPath + laFiles[1] + CHR(0), 5) && Имя файла -> в Unicode lnImage = 0 lnStatus = GdipLoadImageFromFile(lcFile, @lnImage) IF lnStatus > 0 = MESSAGEBOX("Ошибка при чтении исходного файла" + STR(lnStatus)) RETURN ENDIF * * Формируем структуру EncoderParameters * hGlobal = 0 IF lnCount = 1 lcEncoderParameters = NULL ELSE hGlobal = GlobalAlloc(0x0040, 4) lcEncoderParameters = BINTOC(1, "4RS") lcEncoderParameters = lcEncoderParameters + EncoderSaveFlag lcEncoderParameters = lcEncoderParameters + BINTOC(1, "4RS") lcEncoderParameters = lcEncoderParameters + BINTOC(4, "4RS") lcEncoderParameters = lcEncoderParameters + BINTOC(hGlobal, "4RS") = SYS(2600, hGlobal, 4, BINTOC(EncoderValueMultiFrame, "4RS")) ENDIF * * Записываем первый кадр файла * lcFile = STRCONV(lcOutputFile + CHR(0), 5) lnStatus = GdipSaveImageToFile(lnImage, lcFile, EncoderClsID_TIFF, lcEncoderParameters) IF lnStatus = 0 IF lnCount > 1 * * Изменяем значение параметра, передаваемого в структуре lcEncoderParameters, * для функции GdipSaveAddImage * = SYS(2600, hGlobal, 4, BINTOC(EncoderValueFrameDimensionPage, "4RS")) * * Организуем цикл для чтения / записи файлов * FOR i = 2 TO lnCount lcFile = STRCONV(lcPath + laFiles[i] + CHR(0), 5) && Имя файла -> в Unicode lnNewImage = 0 lnStatus = GdipLoadImageFromFile(lcFile, @lnNewImage) IF lnStatus > 0 EXIT ENDIF lnStatus = GdipSaveAddImage(lnImage, lnNewImage, lcEncoderParameters) GdipDisposeImage(lnNewImage) IF lnStatus > 0 EXIT ENDIF ENDFOR * * Изменяем значение параметра, передаваемого в структуре lcEncoderParameters, * для функции GdipSaveAdd * = SYS(2600, hGlobal, 4, BINTOC(EncoderValueFlush, "4RS")) * * Дописываем признак конца файла * lnStatus = GdipSaveAdd(lnImage, lcEncoderParameters) ENDIF ENDIF * * Чистим память * GdipDisposeImage(lnImage) IF hGlobal != 0 GlobalFree(hGlobal) ENDIF = MESSAGEBOX(IIF(lnStatus = 0, "OK", "Ошибка" + STR(lnStatus))) В процедуре используется объект Microsoft Common Dialog, позволяющий выбрать произвольное количество файлов для копирования в TIFF файл. После запуска процедуры на выполнение выделите в появившемся диалоговом окне Common Dialog файлы, а затем в диалоге Save As укажите спецификацию (путь и имя) TIFF файла, в который будут помещены выбранные изображения.
Первоначально в память считывается первый файл из списка, возвращённого
Common Dialog. Если выбран только один файл, то
структура EncoderParameters не формируется, а
переменной lcEncoderParameters
присваивается значение NULL.
Если в TIFF файл записывается более одного кадра,
то при помощи функции SYS(2600) значение параметра заменяется на EncoderValueFrameDimensionPage,
после чего в цикле последовательно считываются в память остальные файлы из
списка, а функция GdipSaveAddImage
добавляет полученные изображения к выходному файлу. Функция GdipDisposeImage освобождает занимаемую считанным изображением память, а функция GlobalFree возвращает Windows память, распределённую для передаваемого в структуре параметра функцией GlobalAlloc. Чтение многокадровых TIFF файловПосле считывания многокадрового TIFF файла в память при помощи функции GdipLoadImageFromFile вы должны определить количество размерностей (dimensions) и количество кадров в каждой размерности.
Делается это при помощи функций GdipImageGetFrameDimensionsCount, GdipImageGetFrameDimensionsList и GdipImageGetFrameCount. Функция GdipImageGetFrameDimensionsCount возвращает количество размерностей TIFF файла: lnDim = 0 lnStatus = GdipImageGetFrameDimensionsCount(nativeImage, @lnDim) Функция GdipImageGetFrameDimensionsList заполняет массив значениями GUID размерностей. Перед её вызовом вы должны создать строку для сохранения возвращаемых GUID (т.е. длина этой строки д.б. равна 16 байтам, умноженным на количество размерностей TIFF файла): lcDimensionIDs = REPLICATE(CHR(0), 16 * lnDim) lnStatus = GdipImageGetFrameDimensionsList(nativeImage, @lcDimensionIDs, lnDim) Каждая размерность TIFF файла может содержать произвольное количество кадров. Для того, чтобы получить доступ к этим кадрам, вы должны извлечсь из lcDimensionIDs строку длиной 16 байт конкретного GUID; так, GUID первой размерности располагается, начиная с первого байта строки, GUID второй размерности располагается, начиная с 17-го байта, и т.д. Функция GdipImageGetFrameCount возвращает количество кадров выбранной размерности: lcDimensionID = SUBSTR(lcDimensionIDs, 1, 16) && Извлекаем GUID первой размерности lnCount = 0 lnStatus = GdipImageGetFrameCount(nativeImage, lcDimensionID, @lnCount) Если вы собираетесь извлекать кадры из TIFF файла, созданного средствами GDI+, то, вместо применения рассмотренных выше функций, можете воспользоваться заранее предопределённым значением для lcDimensionID: #DEFINE FrameDimensionPage 0h86DC627480617E4C8E3FEE7333A7A483 * Здесь ваш код загрузки TIFF-файла в память lnCount = 0 lnStatus = GdipImageGetFrameCount(nativeImage, FrameDimensionPage, @lnCount)так как GDI+ создаёт одноразмерные TIFF файлы. Функция GdipImageSelectActiveFrame перемещает указатель внутри области, в которую загружено изображение, на указанный кадр: lnStatus = GdipImageSelectActiveFrame(nativeImage, lcDimensionID, nFrame) Номер кадра передаётся функции в переменной nFrame. Нумерация кадров начинается с нуля. После установки указателя на нужный кадр вы можете сохранить его при помощи функции GdipSaveImageToFile или выполнить иные действия, разрешённые в GDI+ (нарисовать в окне формы, распечатать на принтере, рисовать на этом кадре и т.д.).
В
листинге 2 приведён код процедуры, извлекающей кадры из TIFF
файла
#DEFINE EncoderClsID_PNG 0h06F47C55041AD3119A730000F81EF32E LOCAL i, j, lcInputFile, lcOutFolder, lcFile, lnImage, lnCount, lnDim, lnStatus LOCAL lcDimensionIDs, lcDimensionID DECLARE Long GdipImageGetFrameDimensionsCount IN Gdiplus.dll Long, Long @ DECLARE Long GdipImageGetFrameDimensionsList IN Gdiplus.dll Long, String @, Long DECLARE Long GdipImageGetFrameCount IN Gdiplus.dll Long, String, Long @ DECLARE Long GdipImageSelectActiveFrame IN Gdiplus.dll Long, String, Long DECLARE Long GdipLoadImageFromFile IN Gdiplus.dll String, Long @ DECLARE Long GdipDisposeImage IN Gdiplus.dll Long DECLARE Long GdipSaveImageToFile IN Gdiplus.dll Long, String, String, String lcInputFile = GETFILE("tif") IF EMPTY(lcInputFile) RETURN ENDIF lcOutFolder = GETDIR() IF EMPTY(lcOutFolder) RETURN ENDIF lcFile = STRCONV(lcInputFile + CHR(0), 5) lnImage = 0 lnStatus = GdipLoadImageFromFile(lcFile, @lnImage) IF lnStatus > 0 = MESSAGEBOX("Ошибка чтения файла" + STR(lnStatus)) RETURN ENDIF lnDim = 0 lnStatus = GdipImageGetFrameDimensionsCount(lnImage, @lnDim) IF lnStatus = 0 lcDimensionIDs = REPLICATE(CHR(0), 16 * lnDim) lnStatus = GdipImageGetFrameDimensionsList(lnImage, @lcDimensionIDs, lnDim) ENDIF FOR j = 1 TO lnDim && Сканирование размерностей IF lnStatus = 0 lcDimensionID = SUBSTR(lcDimensionIDs, 16 * (j - 1) + 1, 16) lnCount = 0 lnStatus = GdipImageGetFrameCount(lnImage, lcDimensionID, @lnCount) IF lnStatus = 0 FOR i = 1 TO lnCount && Сканирование кадров в размерности lnStatus = GdipImageSelectActiveFrame(lnImage, lcDimensionID, i-1) IF lnStatus = 0 lcFile = lcOutFolder + "Output" + LTRIM(STR(j)) + LTRIM(STR(i)) + ".png" lcFile = STRCONV(lcFile + CHR(0), 5) lnStatus = GdipSaveImageToFile(lnImage, lcFile, EncoderClsID_PNG, NULL) IF lnStatus > 0 EXIT ENDIF ENDIF ENDFOR ENDIF ENDIF ENDFOR GdipDisposeImage(lnImage) = MESSAGEBOX(IIF(lnStatus = 0, "OK", "Ошибка" + STR(lnStatus))) После запуска процедуры в диалоговом окне
Open, вызываемом функцией GetFile(),
выберите TIFF файл, и затем в
диалоговом окне, вызываемом функцией Getdir(),
укажите папку для сохранения кадров в виде отдельных файлов. Использование алгоритмов сжатияДля формата TIFF
вы можете использовать один из следующих алгоритмов сжатия:
В GDI+ по умолчанию используется алгоритм сжатия LZW. Алгоритмы CCITT3, CCITT4 и Rle могут применяться только к монохромным файлам (чёрно-белым), в которых для хранения пикселя используется один бит. По умолчанию в GDI+ применяется сжатие по алгоритму LWZ. Если вы хотите отменить сжатие или использовать другой алгоритм, то добавьте в структуру EncoderParameters вложенную структуру, полю Guid которой присвойте значение EncoderCompression.
В следующем фрагменте кода показано формирование структуры EncoderParameters для создания многокадрового TIFF без сжатия исходных файлов. #DEFINE EncoderSaveFlag 0hFC66222940ACBF478CFCA85B89A655DE #DEFINE EncoderCompression 0h9D739DE0D4CCEE448EBA3FBF8BE4FC58 #DEFINE EncoderValueMultiFrame 18 #DEFINE EncoderValueFlush 20 #DEFINE EncoderValueFrameDimensionPage 23 #DEFINE EncoderValueCompressionNone 6 hGlobalMulti = GlobalAlloc(0x0040, 4) && Распределяем глобальную память hGlobalCompr = GlobalAlloc(0x0040, 4) && = SYS(2600, hGlobalMulti, 4, BINTOC(EncoderValueMultiFrame, "4RS") = SYS(2600, hGlobalCompr, 4, BINTOC(EncoderValueCompressionNone, "4RS") lcEncoderParams = BINTOC(2, "4RS") && 2 вложенных структуры lcEncoderParams = lcEncoderParams + EncoderSaveFlag && Поле Guid 1-й структуры lcEncoderParams = lcEncoderParams + BINTOC(1, "4RS") && Поле NumberOfValues lcEncoderParams = lcEncoderParams + BINTOC(4, "4RS") && Поле Type lcEncoderParams = lcEncoderParams + BINTOC(hGlobalMulti, "4RS") && Поле Value lcEncoderParams = lcEncoderParams + EncoderCompression && Поле Guid 2-й структуры lcEncoderParams = lcEncoderParams + BINTOC(1, "4RS") && Поле NumberOfValues lcEncoderParams = lcEncoderParams + BINTOC(4, "4RS") && Поле Type lcEncoderParams = lcEncoderParams + BINTOC(hGlobalCompr, "4RS") && Поле Value Как видите, для каждой вложенной структуры распределяется глобальная память Windows; указатели на распределённые блоки хранятся в переменных hGlobalMulti и hGlobalCompr. Не забудьте вернуть эту память после создания TIFF файла! Управление количеством бит на пиксельНе все графические форматы поддерживают возможность хранения информации в формате с произвольным количеством бит на пиксель. Пожалуй, единственным исключением является формат PNG. В случае, если указанное количество бит на пиксель является недопустимым, то при выполнении вы получите ошибку с кодом 2 (недопустимое значение передаваемого параметра). Для управления количеством бит на пиксель вы должны добавить в структуру EncoderParameters вложенную структуру, присвоив её полю Guid значение EncoderColorDept. Так же распределите для этой структуры блок памяти, указатель на которую поместите в поле Value. Запишите в распределённую память значение количества бит на пиксель.
|