|
#1
|
||||
|
||||
TBitmap и TThread
Итак:
Подскажите пожалуйста, если кто знает, как правильно использовать TThread для того, чтобы вынести функцию наложения фльтра в отдельный поток. Вот код фильтра Sepia: Код:
function BmpToSepia(const Bmp: TBitmap; Depth: Integer): Boolean; var Color,Color2: LongInt; r,g,b,rr,gg: Byte; h,w: Integer; begin for h := 0 to Bmp.Height do begin Filters.ProgressBar.StepIt; Filters.ProgressBar.Update; for w := 0 to Bmp.Width do begin //сначала конвертируем bmp в черно-белые цвета Color := ColorToRGB(Bmp.Canvas.Pixels[w,h]); r := GetRValue(Color); g := GetGValue(Color); b := GetBValue(Color); Color2 := (r + g + b) div 3; Bmp.Canvas.Pixels[w,h] := RGB(Color2,Color2,Color2); //затем ковертируем в Сепию Color := ColorToRGB(Bmp.Canvas.Pixels[w,h]); r := GetRValue(Color); g := GetGValue(Color); b := GetBValue(Color); rr := r + (Depth*2); gg := g + Depth; if rr <= ((Depth*2)-1) then rr := 255; if gg <= (Depth-1) then gg := 255; Bmp.Canvas.Pixels[w,h] := RGB(rr,gg,b); end; end; Result := True; end; Depth = 20; Создал потомка от TThread: Код:
unit SepiaThread; interface uses Classes, SysUtils, Windows, Graphics; type TSepiaThread = class(TThread) private EBmp: Graphics.TBitmap { Private declarations } protected procedure UpdatePrb; procedure Execute; override; function BmpToSepia(const Bmp: TBitmap; Depth: Integer): Boolean; public constructor Create(bmpBitmap: Graphics.TBitmap); end; implementation uses MainForm; constructor TSepiaThread.Create(bmpBitmap: Graphics.TBitmap); begin // как правильно: //EBmp := bmpBitmap; // или //EBmp := Graphics.TBitmap.Create(); //EBmp.Assign(bmpBitmap) ? EBmp := bmpBitmap; inherited Create(False); end; procedure TFirstThread.UpdatePrb; begin Main.ProgressBar.StepIt; end; function TFirstThread.BmpToSepia(const Bmp: TBitmap; Depth: Integer): Boolean; var Color,Color2: LongInt; r,g,b,rr,gg: Byte; h,w: Integer; begin for h := 0 to Bmp.Height do begin Filters.ProgressBar.StepIt; Filters.ProgressBar.Update; for w := 0 to Bmp.Width do begin //сначала конвертируем bmp в черно-белые цвета Color := ColorToRGB(Bmp.Canvas.Pixels[w,h]); r := GetRValue(Color); g := GetGValue(Color); b := GetBValue(Color); Color2 := (r + g + b) div 3; Bmp.Canvas.Pixels[w,h] := RGB(Color2,Color2,Color2); //затем ковертируем в Сепию Color := ColorToRGB(Bmp.Canvas.Pixels[w,h]); r := GetRValue(Color); g := GetGValue(Color); b := GetBValue(Color); rr := r + (Depth*2); gg := g + Depth; if rr <= ((Depth*2)-1) then rr := 255; if gg <= (Depth-1) then gg := 255; Bmp.Canvas.Pixels[w,h] := RGB(rr,gg,b); end; end; Result := True; end; procedure TFirstThread.Execute; var c: Integer; begin FreeOnTerminate := True; BmpToSepia(EBmp,20); end; end. Код писал по памяти, по-этому, могут быть опечатки - не ругайте. Так вот, в результате таких действий: Код:
Unit MainForm; ... ... uses SepiaThread procedure... ... var Thread: TSepiaThread; begin Thread := TSepiaThread.Create(Image1.Picture.Bitmap); end; Мне кажется, что все дело в том, что я либо не присваиваю Image1.Picture.Bitmap'у правильное значение либо как-то не так присваиваю внутренней переменной типа TBitmap потока входящую TBitmap... Помогите пожалуйста разобраться. THE CRACKER IS OUT THERE |
#2
|
||||
|
||||
Частично ришил твою проблему
sepia.zip Частично, потому, что не всегда прога нормально срабатывает с первого раза. Запусти ее, нажми на кнопку, и если в Image2 не отрисуется преобразованная картинка, нажми кнопку еще раз. Такое случается не всегда (я пока не понял, почему это происходит), и максимум со второго раза (по крайней мере у меня) прога срабатывает как надо. В общем, поэксперементируй... |
#3
|
||||
|
||||
Спасибо.
Потестил: После загрузки битмапки надо делаь так: bm.PixelFormat := pf24bit; Чтобы фильтр корректно ложился, а то у меня картинка становится черно-белой =) А вообще, я-то на этом и остановился, о том иречь - что то фильтр применяется, то нет, и в чем проблема- не могу понять, спасибо за помощь, но, м.б. как-то можно все-таки избежать этого бага? =( THE CRACKER IS OUT THERE |
#4
|
|||
|
|||
Цитата:
Итак, по порядку. Дело в том что TImage и TBitmap изначально не ThreadSafe классы и борланд в хелпах говорит что в многопоточной среде возможны баги. Но не стоит отчаиваться, в Канвасе есть специально приготовленные для нас методы Canvas.Lock и Canvas.Unlock (кажется Lock создает семафор, я особо не вдавался в подробности). Так вот, вызывая Lock мы блокируем доступ к канвасу из других thread'ов, а в конце, когда сделали все что хотели, вызываем unlock. Конкретно по файлу, который выложил Decoding. Внутри TSepiaThread.Execute в самом начале пишем Form1.Image2.Canvas.Lock; а в конце execut'а Form1.Image2.Canvas.Unlock; Да, еще убираем Synchronize( UpdateBmp ); внутри BmpToSepia, вместо него пишем просто UpdateBmp, иначе получим "DeadLock". вот, все бы казалось работает, я запустил, отрисовывает, но на попытке 10й я снова увидел "белый" битмап. Баг опять повторялся, хотя теперь очень редко. Тогда я еще вызвал Bmp.Canvas.Lock; и Bmp.Canvas.unLock; внутри метода BmpToSepia. Вот теперь все работает точно. p.s. Больше бага не замечал, но кто знает, проверьте у себя. уфф, чтото я много написал %) |
Этот пользователь сказал Спасибо AlexZL за это полезное сообщение: | ||
SpectraL (07.03.2015)
|
#5
|
||||
|
||||
Спасибо, AlexZL, вроде бы у меня тоже без ошибок работает.
Бли, и как я сам-то не догадался? =)Совсем недавно как раз читал книгу про семафоры, критические секции и т.д.там как раз методы Lock и Unlock для Canvas'а были разобраны =) Еще раз спасибо всем большое. Если вновь возникнут ошибки - отпишусь. THE CRACKER IS OUT THERE |