Что нового

Как сделать часть пикселей битмапа прозрачными перед сохранением в png?

Gealut

Новичок
Сообщения
38
Репутация
0
В очередной раз прошу о помощи, чтобы разобраться с GDIPlus.

Есть созданный в программе битмап вполне определенного вида:


В верхней части этого битмапа находится некое изображение, в нижней - фон. Стоит задача дать юзеру возможность сохранить такое изображение в png с заменой этого нижнего фона на прозрачность. Высота верхней части изображения известна, ее вычислять не надо.

В спойлере упрощенный код скрипта, отвечающий за создание и сохранение в файл этого битмапа. Упрощенный - в том смысле, что создание такого варианта файла и сохранение его в png есть лишь один из вариантов возможных действий, но с вариантами создания файла без прозрачного фона и с сохранением его в другие форматы у меня уже проблем нет. Проблема только с созданием прозрачности. Поэтому я убрал из кода все дополнительные варианты действий, оставил только те части функций, которыми создается такой файл и сохраняется.

Код:
...
$hBMP = _GetImage($CurCover, $PreviewSizeW, $PreviewSizeH, $NewSizeW, $NewSizeH)
_hBmpToFile($CurRename, $hBMP)
...

Func _GetImage($sFile, $pW, $pH, $vW, $vH, $iBkClr = 0xFFFFFF)
    Local $hBmp1, $hBitmap, $hGraphic, $hImage, $iW, $iH, $aGS, $hBmp2
    _GDIPlus_Startup()
    $hImage = _GDIPlus_ImageLoadFromFile($sFile)
    $iW = _GDIPlus_ImageGetWidth($hImage)
    $iH = _GDIPlus_ImageGetHeight($hImage)
	$aGS[3] = $vH
	$aGS[2] = Round(($iW/$iH) * $vH)
	$aGS[0] = 0
	$aGS[1] = 0
	If $aGS[2] > $pW Then $aRet[0] = (($pW - $aRet[2]) / 2)
	$hBmp1 = _WinAPI_CreateBitmap($pW, $pH, 1, 32)
    $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBmp1)
    $hGraphic = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _WinAPI_DeleteObject($hBmp1)
	_GDIPlus_GraphicsClear($hGraphic, BitOR(0xFF000000, $iBkClr))
	_GDIPlus_GraphicsDrawImageRect($hGraphic, $hImage, $aGS[0], $aGS[1], $aGS[2], $aGS[3])

;	$hBitmap2 = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iW, $iH, $GDIP_PXF32ARGB)
;	_GDIPlus_BitmapDispose($hBitmap)
;	$tBitmapData = _GDIPlus_BitmapLockBits($hBitmap2, 0, $aGS[3] + 1, $iW, $iH, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
;	$tPixels = DllStructCreate("byte[" & $iH * $iW * 4 & "]", DllStructGetData($tBitmapData, "Scan0")) ; Create DLL structure for all pixels
;	DllStructSetData($tPixels, 1, StringReplace(DllStructGetData($tPixels, 1), "000000FF", "00000000"))
;	_GDIPlus_BitmapUnlockBits($hBitmap2, $tBitmapData)
;	$tPixels = 0
;	$tBitmapData = 0

    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_GraphicsDispose($hGraphic)
	
;		$hBmp2 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap2)
;		_GDIPlus_BitmapDispose($hBitmap2)

	$hBmp2 = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
	_GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()
    Return $hBmp2
EndFunc


Func _hBmpToFile($fName, ByRef $hBMP)
	_GDIPlus_Startup()
	$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)
	_GDIPlus_ImageSaveToFile($hImage, $fName)
	_WinAPI_DeleteObject($hBMP)
	_GDIPlus_ImageDispose($hImage)
	_GDIPlus_Shutdown()
EndFunc

Поиском я нашел (в основном, на английском форуме) примеры как заменять пиксели на прозрачный фон), но не смог нормально понять этот код, а попытка скопировать чужой с какой-то минимальной правкой успеха не дала. Эти вставки в функции закомментарены.

Хочется разобраться. Я понял, что надо изменить атрибуты у пикселей изображения на $GDIP_PXF32ARGB, чтобы задействовать альфа-канал. Далее, похоже, создается клон изображения, пиксели, которые будут правиться лочатся, потом в них регулярными выражениями заменяется цвет с включением прозрачности. С каждым шагом понятно все меньше.

Подскажите, пожалуйста. Желательно, с работающим примером.

Спасибо.
 
Автор
G

Gealut

Новичок
Сообщения
38
Репутация
0
Решил проблему сам путем перемены необходимых действий местами. Сначала создаю битмап, добавляю в него альфа-канал, делаю все пиксели прозрачными. А потом уже поверх части этого битмапа переписываю содержимое джипега. И сохраняю в PNG. В результате получается PNG файл, в котором фон вокруг картинки прозрачный. То, что и требовалось.

Пример тестового скрипта, если вдруг кому-то потребуется аналогичное:

Код:
#include <WinAPI.au3>
#include <GDIPlus.au3>

$pW = 380
$pH = 694

_GDIPlus_Startup()
$hBMP1 = _WinAPI_CreateBitmap($pW, $pH , 1 , 32)
$hBMP = _GDIPlus_BitmapCreateFromHBITMAP($hBMP1)
_WinAPI_DeleteObject($hBMP1)
$hBitmap = _GDIPlus_BitmapCloneArea($hBMP, 0, 0, $pW, $pH, $GDIP_PXF32ARGB)
_GDIPlus_BitmapDispose($hBMP)
$tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $pW, $pH, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
$tPixels = DllStructCreate("byte[" & $pH * $pW * 4 & "]", DllStructGetData($tBitmapData, "Scan0"))
DllStructSetData($tPixels, 1, StringReplace(DllStructGetData($tPixels, 1), "000000FF", "00000000"))
_GDIPlus_BitmapUnlockBits($hBitmap, $tBitmapData)
$tPixels = 0
$tBitmapData = 0
$hGraphic = _GDIPlus_ImageGetGraphicsContext($hBitmap)
$hImage = _GDIPlus_ImageLoadFromFile(@WorkingDir & "test.jpg")
$iW = _GDIPlus_ImageGetWidth($hImage)
$iH = _GDIPlus_ImageGetHeight($hImage)
_GDIPlus_GraphicsDrawImageRect($hGraphic, $hImage, 0, 0, $iW, $iH)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_GraphicsDispose($hGraphic)
_GDIPlus_ImageSaveToFile($hBitmap, @WorkingDir & "\result.png")
_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_Shutdown()
 
Верх