Разбивка GIF на PNG с сохранением альфа канала - VB
Формулировка задачи:
Собственно в названии темы и сама суть вопроса
Нашел алгоритм разбивки GIF
но одно большое НО - изображения (отличные от первого) получают размер не GIF, а самой картинки, обрезанной по краям до первых цветных пикселей (т.е. лишний фон без содержания удаляется)
Это алгоритм формата GIF для уменьшения размера файла?
Или алгоритм изъятия имеет свои минусы ? (хотя я не увидел там обрезки лишнего...)
исходники прилагаю
Решение задачи: «Разбивка GIF на PNG с сохранением альфа канала»
textual
Листинг программы
Option Explicit Private Type GdiplusStartupInput ' Структура инициализации GDI+ GdiplusVersion As Long DebugEventCallback As Long SuppressBackgroundThread As Long SuppressExternalCodecs As Long End Type Private Type CLSID Data1 As Long Data2 As Integer Data3 As Integer Data4(0 To 7) As Byte End Type Public Type PropertyItem ' Свойство изображения propId As Long Length As Long type As Long value As Long End Type Private Type RECT iLeft As Long iTop As Long iRight As Long iBottom As Long End Type Private Type PAINTSTRUCT hdc As Long fErase As Long rcPaint As RECT fRestore As Long fIncUpdate As Long rgbReserved(31) As Byte End Type Private Declare Function GdipLoadImageFromFile Lib "gdiplus" (ByVal FileName As String, image As Long) As Long Private Declare Function GdiplusShutdown Lib "gdiplus" (ByVal token As Long) As Long Private Declare Function GdiplusStartup Lib "gdiplus" (token As Long, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As Long Private Declare Function GdipImageGetFrameCount Lib "gdiplus" (ByVal image As Long, dimensionID As CLSID, Count As Long) As Long Private Declare Function CLSIDFromString Lib "ole32.dll" (ByVal lpszProgID As Long, pCLSID As CLSID) As Long Private Declare Function GdipDisposeImage Lib "gdiplus" (ByVal image As Long) As Long Private Declare Function GdipImageSelectActiveFrame Lib "gdiplus" (ByVal image As Long, dimensionID As CLSID, ByVal frameIndex As Long) As Long Private Declare Function GdipGetPropertyItemSize Lib "gdiplus" (ByVal image As Long, ByVal propId As Long, size As Long) As Long Private Declare Function GdipGetPropertyItem Lib "gdiplus" (ByVal image As Long, ByVal propId As Long, ByVal propSize As Long, buffer As Any) As Long Private Declare Function GdipGetPropertySize Lib "gdiplus" (ByVal image As Long, totalBufferSize As Long, numProperties As Long) As Long Private Declare Function GdipDrawImage Lib "gdiplus" (ByVal Graphics As Long, ByVal image As Long, ByVal x As Single, ByVal y As Single) As Long Private Declare Function GdipCreateFromHDC Lib "gdiplus" (ByVal hdc As Long, Graphics As Long) As Long Private Declare Function GdipDeleteGraphics Lib "gdiplus" (ByVal Graphics As Long) As Long Private Declare Function GdipGetImageWidth Lib "gdiplus" (ByVal image As Long, Width As Long) As Long Private Declare Function GdipGetImageHeight Lib "gdiplus" (ByVal image As Long, Height As Long) As Long Private Declare Function GdipGraphicsClear Lib "gdiplus" (ByVal Graphics As Long, ByVal lColor As Long) As Long Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) Private Declare Function GetMem2 Lib "msvbvm60" (pSrc As Any, pDst As Any) As Long Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Private Declare Function BeginPaint Lib "user32" (ByVal hwnd As Long, lpPaint As PAINTSTRUCT) As Long Private Declare Function EndPaint Lib "user32" (ByVal hwnd As Long, lpPaint As PAINTSTRUCT) As Long Private Declare Function RedrawWindow Lib "user32" (ByVal hwnd As Long, lprcUpdate As Any, ByVal hrgnUpdate As Long, ByVal fuRedraw As Long) As Long Private Const GWL_WNDPROC = (-4) ' Для замены оконной процедуры Private Const WM_PAINT = &HF ' Сообщение отрисовки Private Const RDW_INVALIDATE = &H1 ' Объявить недействительно область Private Const FrameDimensionTime As String = "{6AEDBD6D-3FB5-418A-83A6-7F45229DC872}" Private Const PropertyTagFrameDelay = &H5100 ' Идентификатор значения длительности кадров Private Const PropertyTagLoopCount = &H5101 ' Идентификатор значения количества повторов анимации ' Локальные переменные уровня модуля Dim mHwnd As Long, Init As Boolean, token As Long, img As Long, frames As Long, gr As Long, prevwndproc As Long Dim tInit As Boolean, frame() As Long, loops As Long, index As Long, cycle As Long, isplay As Boolean ' Хендл окна в котором будем рисовать анимацию Public Property Get hwnd() As Long hwnd = mHwnd End Property Public Property Let hwnd(ByVal value As Long) StopAnim ' Останавливаем анимацию If hwnd Then UnHook ' Если до этого сабклассили, то отключаем сабклассинг mHwnd = value Hook ' Сабклассим новое окно End Property ' Ширина кадра анимации Public Property Get Width() As Long GdipGetImageWidth img, Width End Property ' Высота кадра анимации Public Property Get Height() As Long GdipGetImageHeight img, Height End Property ' Текущий кадр Public Property Get CurrentFrame() As Long CurrentFrame = index End Property ' Количество кадров Public Property Get FramesCount() As Long FramesCount = frames End Property ' Сколько циклов анимации Public Property Get LoopCount() As Long LoopCount = loops End Property ' Проигрывается ли анимация Public Property Get IsPlaying() As Boolean IsPlaying = isplay End Property ' Загрузка файла анимации Public Function LoadAnimation(ByVal FileName As String) As Boolean Dim GpInput As GdiplusStartupInput ' Для инициализации GDI+ Dim pc As Long, sz As Long, pi As PropertyItem Dim buf() As Byte ' Буффер для свойств If Not Init Then ' Если не инициализирован GDI+ GpInput.GdiplusVersion = 1 If GdiplusStartup(token, GpInput) Then Exit Function Else Init = True ' Инициализируем, при неудаче выходим End If Clear ' Очистка, если до этого что-то загружали If GdipLoadImageFromFile(StrConv(FileName, vbUnicode), img) Then Exit Function ' Загружаем картинку If GdipImageGetFrameCount(img, DEFINE_GUID(FrameDimensionTime), frames) Then ' Проверяем кол-во кадров GdipDisposeImage img ' При неудаче удаляем картинку и выходим (возможно что не GIF) Exit Function End If ' Узнаем время каждого кадра GdipGetPropertyItemSize img, PropertyTagFrameDelay, sz ' Получаем размер свойства в байтах If sz > 0 And frames > 1 Then ' Имеет смысл только если кадров>1 ReDim buf(sz - 1) ' Выделяем буфер GdipGetPropertyItem img, PropertyTagFrameDelay, sz, buf(0) ' Копируем свойство в буфер CopyMemory pi, buf(0), Len(pi) ' Копируем в описатель ReDim frame(frames - 1) ' Выделяем кадровый буфер (длительности) CopyMemory frame(0), buf(Len(pi)), pi.Length ' Копируем значения длительностей End If ' Узнаем зациклена ли анимация GdipGetPropertyItemSize img, PropertyTagLoopCount, sz If sz > 0 And frames > 1 Then ReDim buf(sz - 1) GdipGetPropertyItem img, PropertyTagLoopCount, sz, buf(0) CopyMemory pi, buf(0), Len(pi) GetMem2 buf(Len(pi)), loops End If index = 0: cycle = 0 ' Очистка счетчиков LoadAnimation = True ' Успешно End Function ' Проигрыш анимации Public Function PlayAnim() As Boolean If Init And hwnd <> 0 And img <> 0 And Not isplay Then If frames > 1 Then ' Смысл запускать таймер если кадров > 1 If SetTimer(mHwnd, 1, frame(index) * 10, AddressOf TimerProc) = 0 Then _ Exit Function ' Не удалось запустить таймер End If tInit = True ' Таймер инициализирован isplay = True ' Включен проигрыш PlayAnim = True ' Возвращаем успешное значение RedrawWindow mHwnd, ByVal 0, 0, RDW_INVALIDATE ' Перерисовываем End If End Function ' Пауза Public Function Pause() As Boolean If isplay Then ' Если играем isplay = False ' то останавливаем Pause = True ' Возвращаем успешное значение StopTimer ' Останавливаем таймер End If End Function ' Остановка анимации Public Sub StopAnim() isplay = False ' Останавливаем проигрывание index = 0 ' Обнуляем текущий кадр cycle = 0 ' Обнуляем счетчик циклов StopTimer ' Останавливаем таймер RedrawWindow mHwnd, ByVal 0, 0, RDW_INVALIDATE ' Перерисовываем End Sub ' Завершение работы Public Sub ShutDown() Clear ' Очистка ресурсов If mHwnd Then UnHook ' Если сабклассили то выключаем сабклассинг GdiplusShutdown token ' Выключаем GDI+ End Sub ' Очистка ресурсов Private Sub Clear() frames = 0 ' Обнуляем количество кадров Erase frame() ' Удаляем массив длительностей кадров If img Then GdipDisposeImage img ' Удаляем картинку StopAnim ' Останавливаем анимацию End Sub ' Остановка таймера Private Sub StopTimer() If tInit Then ' Если таймер инициализирован KillTimer mHwnd, 1 ' Удаляем его tInit = False ' Таймер не инициализирован End If End Sub ' Из строки в GUID Private Function DEFINE_GUID(ByVal sGuid As String) As CLSID Call CLSIDFromString(StrPtr(sGuid), DEFINE_GUID) ' GUID из строкового параметра End Function ' Сабклассинг Private Sub Hook() prevwndproc = SetWindowLong(mHwnd, GWL_WNDPROC, AddressOf WndProc) ' Назначаем свою оконную процедуру End Sub Private Sub UnHook() SetWindowLong mHwnd, GWL_WNDPROC, prevwndproc ' Возвращаем оконную процедуру End Sub ' Оконная процедура Private Function WndProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Dim ps As PAINTSTRUCT, tdc As Long, tbmp As Long, obmp As Long, rc As RECT Select Case Msg Case WM_PAINT ' Отрисовка If index = -1 Or Not Init Then WndProc = CallWindowProc(prevwndproc, hwnd, Msg, wParam, lParam) ' Если нет активной анимации рисуем как было Else ' Для предотвращения мерцания, например анимаций с черным фоном, я решилл использовать двойную буфферизацию GetClientRect hwnd, rc ' Узнаем размер ко контрола BeginPaint hwnd, ps tdc = CreateCompatibleDC(ps.hdc) ' Буфферный DC tbmp = CreateCompatibleBitmap(ps.hdc, rc.iRight, rc.iBottom) ' Буфферная картинка obmp = SelectObject(tdc, tbmp) GdipCreateFromHDC tdc, gr ' Создаем Graphics GdipGraphicsClear gr, &HFFFFFFFF ' Заливаем белым цветом GdipImageSelectActiveFrame img, DEFINE_GUID(FrameDimensionTime), index ' Выбираем текущий кадр GdipDrawImage gr, img, 0, 0 ' Рисуем его GdipDeleteGraphics gr ' Удаляем Graphics BitBlt ps.hdc, 0, 0, rc.iRight - rc.iLeft, _ rc.iBottom - rc.iTop, tdc, 0, 0, vbSrcCopy ' Отрисовка из буфера SelectObject tdc, obmp ' Восстанавливаем и удаляем .... DeleteObject tbmp DeleteDC tdc EndPaint hwnd, ps End If Case Else WndProc = CallWindowProc(prevwndproc, hwnd, Msg, wParam, lParam) ' Остальное нас не интересует End Select End Function ' Процедура таймера Private Sub TimerProc(ByVal hwnd As Long, ByVal Msg As Long, idEvent As Long, ByVal dwTime As Long) index = index + 1 ' Кадр увеличился If index >= frames Then ' Если переаолнение кадров index = 0 ' В начало cycle = cycle + 1 ' Увеличиваем циклы If cycle > loops And CBool(loops) Then StopAnim: Exit Sub ' Если переполнение циклов то выключаем анимацию End If RedrawWindow mHwnd, ByVal 0, 0, RDW_INVALIDATE ' Перерисовываем If SetTimer(mHwnd, 1, frame(index) * 10, AddressOf TimerProc) = 0 Then ' Устанавливаем новую задержку StopAnim ' Не удалось запустить таймер End If End Sub
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д