![]() |
|
|
#1
|
||||
|
||||
|
Итак:
Подскажите пожалуйста, если кто знает, как правильно использовать 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... Помогите пожалуйста разобраться. |
|
#2
|
||||
|
||||
|
Частично ришил твою проблему
sepia.zip Частично, потому, что не всегда прога нормально срабатывает с первого раза. Запусти ее, нажми на кнопку, и если в Image2 не отрисуется преобразованная картинка, нажми кнопку еще раз. Такое случается не всегда (я пока не понял, почему это происходит), и максимум со второго раза (по крайней мере у меня) прога срабатывает как надо. В общем, поэксперементируй... |
|
#3
|
||||
|
||||
|
Спасибо.
Потестил: После загрузки битмапки надо делаь так: bm.PixelFormat := pf24bit; Чтобы фильтр корректно ложился, а то у меня картинка становится черно-белой =) А вообще, я-то на этом и остановился, о том иречь - что то фильтр применяется, то нет, и в чем проблема- не могу понять, спасибо за помощь, но, м.б. как-то можно все-таки избежать этого бага? =( |
|
#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'а были разобраны =) Еще раз спасибо всем большое. Если вновь возникнут ошибки - отпишусь. |