Что нового

Очень интересный глюк с _GDIPlus_ImageGetWidth и _GDIPlus_ImageGetHeight

vaf

Новичок
Сообщения
189
Репутация
2
Добрый день уважаемые. Понадобилось мне тут в цикле произвести некоторые вычисления, для которых мне нужны были значения
_GDIPlus_ImageGetWidth и _GDIPlus_ImageGetHeight, но столкнулся с необъяснимой проблемой.
_GDIPlus_ImageGetWidth и _GDIPlus_ImageGetHeight выдавал мне немыслимые размеры *.jpg файлов, а именно 4294967295

Целый день ломал голову, но так и не понял из за чего проблема и как ее решить. Стал искать причины.
И выяснилось следующее

Вот этот код, начинает выдавать немыслимые значения ширины , а именно "4294967295"
Код:
For $y = 1 To 6
	  For $i = 0 To UBound ($1) - 1
		 _GDIPlus_Startup()
		 $xx = _GDIPlus_ImageGetWidth (_GDIPlus_ImageLoadFromFile($path & $1[$i]))
		 $yy = _GDIPlus_ImageGetHeight (_GDIPlus_ImageLoadFromFile($path & $1[$i]))
; тут у меня идут математические вычисления с $y, не затрагивая _GDIPlus, но суть не в этом
		 FileWriteLine ("222.txt", $1[$i] & " " & $xx & "x" & $yy)
		 _GDIPlus_ShutDown ()
 	  Next
   Next


Стал экспериментировать и искать проблему и обнаружилось, что, убрав первый цикл, выдает нормальные значения.

Код:
For $i = 0 To UBound ($1) - 1
		 _GDIPlus_Startup()
		 $xx = _GDIPlus_ImageGetWidth (_GDIPlus_ImageLoadFromFile($path & $1[$i]))
		 $yy = _GDIPlus_ImageGetHeight (_GDIPlus_ImageLoadFromFile($path & $1[$i]))
		 FileWriteLine ("111.txt", $1[$i] & " " & $xx & "x" & $yy)
		 _GDIPlus_ShutDown ()
 	  Next


Вопрос: Чем помешал цикл ? даже пустой. И еще, чем больше первый цикл, тем раньше начинает глючить. Например при первом цикле до 6, глючить начинает произведя 9500 вычислений, а при цикле до 7 глючит произведя 8000 вычислений.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
vaf [?]
Вот этот код, начинает выдавать немыслимые значения ширины

Т.к. это не код, а кусок кода, то "немыслимые значения ширины" он выдает только у вас.

У меня, такой код, работает без ошибок :
Код:
#Include <GDIPlus.au3>

$sFile = @ScriptDir & '\test.png'
If Not FileExists($sFile) Then
	Exit
EndIf

For $i = 0 To 10000
	_GDIPlus_Startup()
	$xx = _GDIPlus_ImageGetWidth(_GDIPlus_ImageLoadFromFile($sFile))
	$yy = _GDIPlus_ImageGetHeight(_GDIPlus_ImageLoadFromFile($sFile))
	ConsoleWrite('->' & $xx & 'x' & $yy & @CRLF)
	_GDIPlus_Shutdown()
Next


Так что, ошибка, практически наверняка, находится в том куске кода, который вы не выложили.

ps: для поиска подобных ошибок, скрипт желательно запускать с параметром :
Код:
Opt('MustDeclareVars', 1)
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
vaf,
asdf8,
Нельзя так издеваться над GDIPlus.dll и компьютером. :smile:
Попробуйте так.
Код:
#include <GDIP.au3> ;http://autoit-script.ru/index.php/topic,1384.0.html
#include <File.au3>

Global $sPath = 'W:\рисунки\', $aFiles, $hImage, $aSize, $iX, $iY, $sFile = 'Test.txt', $hFile, _
		$sFormat = '%s.%s. %s:\t%sx%s\r\n'

$aFiles = _FileListToArray($sPath, '*.jpg', 1)
If @error Then Exit 13

_GDIPlus_Startup()
$hFile = FileOpen(@ScriptDir & '\' & $sFile, 1)

For $i = 1 To $aFiles[0]
	$hImage = _GDIPlus_ImageLoadFromFile($sPath & $aFiles[$i])
	If @error Then ContinueLoop
	$aSize = _GDIPlus_ImageGetDimension($hImage)
	If Not @error Then
		For $j = 1 To 6
			$iX = $aSize[0] + ($j - 1) * 10;some calculations
			$iY = $aSize[1] + ($j - 1) * 5;some calculations
			FileWrite($hFile, StringFormat($sFormat, $i, $j, $aFiles[$i], $iX, $iY))
		Next
		FileWrite($hFile, @CRLF)
	EndIf
	_GDIPlus_ImageDispose($hImage)
Next
FileClose($hFile)
_GDIPlus_Shutdown()
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
vaf сказал(а):
Чем помешал цикл?

Если бы у вас было больше практики, то вы бы сразу сообразили, что число 4294967295 есть (-1) в беззнаковом представлении, т.е. функция возвращает ошибку. Причина в типе данных и глупости метода возвращения ошибок:

Код:
$aResult = DllCall($ghGDIPDll, "int", "GdipGetImageWidth", "handle", $hImage, "uint*", -1)


vaf сказал(а):
И еще, чем больше первый цикл, тем раньше начинает глючить.

Дескрипторы необходимо освобождать.
 
Автор
V

vaf

Новичок
Сообщения
189
Репутация
2
madmasles спасибо за альтернативное решение, но и в этом решении есть проблема, я переделал ваш код под себя и в моем цикле строго на определенном цикле
Код:
$aSize = _GDIPlus_ImageGetDimension($hImage)
не выдает массив, хотя файл существует, все нормально. переношу этот файл выше или вообще его удаляю, все равно выдает ошибку на определенном цикле. Такое ощущение что он сколько то отработал и начинает глючить.
ну и т.к. $aSize уже не массив, дальше вываливается просто в ошибку, т.к. у меня дальше операции масштабирования для $aSize[0], и $aSize[1]
я поставил проверку.
Код:
If Not IsArray ($aSize) Then ...
может это как раз из за того о чем говорил Yashied - про дескрипторы
но у меня начинается обработка начинается с
Код:
$hImage = _GDIPlus_ImageLoadFromFile($Path & $1[$i])
а заканчивается
Код:
_GDIPlus_ImageDispose($hImage)
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
_GDIPlus_ImageGetDimension() может возвращать ошибку только, если $hImage неверно указан, а это свидетельствует о том, что _GDIPlus_ImageLoadFromFile() не смогла прочитать файл.

P.S

То, что представил madmasles, самый правильный вариант использования GDI+.
 
Автор
V

vaf

Новичок
Сообщения
189
Репутация
2
Весь код приводить не буду, там много эмуляции мыши и клавиатуры, почистил.
В z:\picture\ картинки
Проблема в том, что $aSize не всегда получается массивом, но если перезапустить скрипт уже с той записи где произошла ошибка, то он какое то время работает нормально и опять вываливается в ошибку, т.е. все файлы читаются.

Пробовал сделать так
Код:
If Not IsArray ($aSize) Then
	  _GDIPlus_ImageDispose($hImage)
	  _GDIPlus_ShutDown ()
	 _GDIPlus_Startup()
EndIf

Какое то время работает и все повторяется, так что пока просто приходится запускать в ручную с того места с какого застопорилось


Код:
#include <GDIP.au3> ;http://autoit-script.ru/index.php/topic,1384.0.html
#include <String.au3>
#include <Array.au3>
#include <File.au3>

$width = 675 ; Ширина области в пикселах
$n = 0 ; С какой записи начинать (0 - сначала)
$html = FileOpenDialog ("Выберите файл для чтения", @ScriptDir, "Все файлы (*.txt)")
$path = "z:\pictures\" ; Здесь содержатся картинки

$rec = _FileCountLines ($html) ; кол-во строк в файле

While 1
   ToolTip ("Всего : " & $rec & ", обрабатывается : " & $n, 0, 0)
   $n+=1
   $ReadLine = FileReadLine ($html, $n)

   $post_text = $ReadLine

   $1 = _StringBetween($post_text,'title="','" src') ; Заносим в массив имена всех картинок
   
   For $y = 1 To 250 ; Цикл на высоту от 1 до 250 точек
	  $tmp = 0
	  For $i = 0 To UBound ($1) - 1 ; Цикл на все картинки
		 _GDIPlus_Startup()
		 $hImage = _GDIPlus_ImageLoadFromFile($Path & $1[$i]) ; Получаем ID картинки
		 $aSize = _GDIPlus_ImageGetDimension($hImage) ; Получаем высоту и ширину картинки $aSize[0], $aSize[1]
		 If Not IsArray ($aSize) Then
			MsgBox (4096, "Ошибка", "Начинайте с " & $n-1)
			Exit
		 EndIf
			
		 $nx = Floor (resize ($aSize[0], $aSize[1], $y)) ; Получаем ширину изображения для высоты Y
		 $tmp = $tmp + $nx
	  Next
	  _GDIPlus_ImageDispose($hImage)
	  _GDIPlus_ShutDown ()
	  If $tmp >= $width Then ExitLoop ; Если общая ширина картинок >= ширине сайта, выходим из цикла. $y = высота картинок
   Next
   If $n = $rec Then  Exit
WEnd

Func resize ($ow, $oh, $nh) ; Функция масштабирования изображения (ширина, высота, новая высота) возвращает получившуюся ширину
   $x = (($ow * $nh) / $oh)
   Return $x
EndFunc
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
А так?

Код:
#include <GDIP.au3> ;http://autoit-script.ru/index.php/topic,1384.0.html
#include <String.au3>
#include <Array.au3>
#include <File.au3>

$width = 675 ; Ширина области в пикселах
$n = 0 ; С какой записи начинать (0 - сначала)
$html = FileOpenDialog("Выберите файл для чтения", @ScriptDir, "Все файлы (*.txt)")
$path = "z:\pictures\" ; Здесь содержатся картинки

_GDIPlus_Startup()

$rec = _FileCountLines($html) ; кол-во строк в файле

While 1
	ToolTip("Всего : " & $rec & ", обрабатывается : " & $n, 0, 0)
	$n += 1
	$ReadLine = FileReadLine($html, $n)

	$post_text = $ReadLine

	$1 = _StringBetween($post_text, 'title="', '" src') ; Заносим в массив имена всех картинок

	For $y = 1 To 250 ; Цикл на высоту от 1 до 250 точек
		$tmp = 0
		For $i = 0 To UBound($1) - 1 ; Цикл на все картинки
			$hImage = _GDIPlus_ImageLoadFromFile($path & $1[$i]) ; Получаем ID картинки
			$aSize = _GDIPlus_ImageGetDimension($hImage) ; Получаем высоту и ширину картинки $aSize[0], $aSize[1]
			If Not IsArray($aSize) Then
				MsgBox(4096, "Ошибка", "Начинайте с " & $n - 1)
				Exit
			EndIf

			$nx = Floor(resize($aSize[0], $aSize[1], $y)) ; Получаем ширину изображения для высоты Y
			$tmp = $tmp + $nx
		Next
		_GDIPlus_ImageDispose($hImage)
		If $tmp >= $width Then ExitLoop ; Если общая ширина картинок >= ширине сайта, выходим из цикла. $y = высота картинок
	Next
	If $n = $rec Then Exit
WEnd

_GDIPlus_Shutdown()

Func resize($ow, $oh, $nh) ; Функция масштабирования изображения (ширина, высота, новая высота) возвращает получившуюся ширину
	$x = (($ow * $nh) / $oh)
	Return $x
EndFunc   ;==>resize
 
Автор
V

vaf

Новичок
Сообщения
189
Репутация
2
Не помогает. Ровно на том же месте, вылетает.
Это 100% не зависит ни от изображений ни от их размера, т.к. копирую в папку другие изображения, ошибка на том же месте.
В общем я думаю нужно делать дополнительный цикл, а когда появляется ошибка, т.е.

Код:
If Not IsArray($aSize) Then
    MsgBox(4096, "Ошибка", "Начинайте с " & $n - 1)
    Exit
EndIf


выходить из этого цикла и начинать все заново с того места, откуда застопорилось (некая эмуляция, того что я делаю вручную, запуская скрипт с того места, где выдало ошибку)

P.S. Сделал. Выдает следующую ошибку : Error Allocating memory
Видимо нехватает памяти, а когда в ручную запускаю, скрипт заново запускается с начала, т.е. ему наверняка и память выдается заново.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
vaf сказал(а):
Выдает следующую ошибку : Error Allocating memory

Проверьте, что всегда вызывается _GDIPlus_ImageDispose() после _GDIPlus_ImageLoadFromFile(). Это единственная причина утечки памяти.
 
Автор
V

vaf

Новичок
Сообщения
189
Репутация
2
Точно, спасибо за совет - помогло. У меня _GDIPlus_ImageDispose() вызывался после целого цикла с участием GDIPlus_ImageLoadFromFile().
Поставил внутри этого цикла и все сразу заработало. Спасибо огромное !!!!
 
Верх