PDA

View Full Version : ایجاد ایمیج جدید



reza_king_sh
سه شنبه 14 مهر 1388, 09:51 صبح
با سلام.
دوستان من یه فرم دارم با یک ایمیج که توش یه عکس گذاشتم و دو تا باتن. می خوام وقتی روی باتن اولی کلیک کردم اندازه و مکان ایمیج و همچنین تصویر اونو تو یه آرایه ذخیره کنم
و بعد اون ایمیج رو پاک کنم. برای این کار یک رکورد تعریف کردم و بعد یه آرایه از نوع اون رکورد تعریف کردم. حالا می خوام با زدن باتن دومی یه ایمیج ایجاد بشه که
خصوصیات اون امیج اولی رو داشته باشه. از این نمونه کد بعداً می خوام تو یه برنامه برای آندو کردن ایمیج های پاک شده استفاده کنم. کد این نمونه برنامه اینه:


unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
Undo = record
UnLeft : Integer;
UnTop : Integer;
UnHeight : Integer;
UnWidth : Integer;
UnPicture : TPicture;
end;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
MyImage: TImage;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
TopStack: Integer;
Unstack: array[1..50] of undo;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TopStack:= 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TopStack:= TopStack + 1;
Unstack[TopStack].UnLeft:= MyImage.Left;
Unstack[TopStack].UnTop:= MyImage.Top;
Unstack[TopStack].UnWidth:= MyImage.Height;
Unstack[TopStack].UnHeight:= MyImage.Width;
Unstack[TopStack].UnPicture:= MyImage.Picture;
MyImage.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
Img: TImage;
begin
if TopStack= 0 then
exit;
Img:= TImage.Create(Self);
Img.Parent:= Form1;
Img.SetBounds(Unstack[TopStack].UnLeft, Unstack[TopStack].UnTop,
Unstack[TopStack].UnWidth, Unstack[TopStack].UnHeight);
Img.Picture.Graphic:= Unstack[TopStack].UnPicture.Graphic;
TopStack:= TopStack - 1;
end;

end.
حالا وقتی رو باتن دومی کلیک میشه ایمیج ایجاد میشه ولی هیچ تصویری نداره و وقتی از برنامه خارج میشم یه خطا با این متن ظاهر میشه:
Exception EInvalidPointer in module Project1.exe at 000027F0.
Invalid pointer operation.
میشه این کد رو تست کنید و ببینید چه شکالی داره. با سپاس.

Felony
سه شنبه 14 مهر 1388, 12:40 عصر
مشکل کد شما این هست که بعد از اینکه عکس Image رو تو رکوردتون ریختید اون Image رو Free میکنید .
در ضمن کدتون رو تست کردم اون پیغام خطایی هم که هنگام خروج گفتید میده رو نداد .

tdkhakpur
سه شنبه 14 مهر 1388, 15:05 عصر
یه خورده اشکال درش هست ولی امنیت کاری نداره باید بعدا اصلاحش کنی


unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
Undo = record
UnLeft : Integer;
UnTop : Integer;
UnHeight : Integer;
UnWidth : Integer;
// UnPicture : TPicture; حذف
UnPicture : TMemoryStream;
end;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
MyImage: TImage;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
TopStack: Integer;
Unstack: array[1..50] of undo;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TopStack:= 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TopStack:= TopStack + 1;
Unstack[TopStack].UnPicture := TMemoryStream.Create; // اضافه

Unstack[TopStack].UnLeft:= MyImage.Left;
Unstack[TopStack].UnTop:= MyImage.Top;
Unstack[TopStack].UnWidth:= MyImage.Height;
Unstack[TopStack].UnHeight:= MyImage.Width;

// Unstack[TopStack].UnPicture:= MyImage.Picture; حذف شود
Unstack[TopStack].UnPicture.Position := 0;
MyImage.Picture.Bitmap.SaveToStream(Unstack[TopStack].UnPicture);

MyImage.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
Img: TImage;
begin
if TopStack= 0 then
exit;
Img:= TImage.Create(Self);
Img.Parent:= Form1;
Img.SetBounds(Unstack[TopStack].UnLeft, Unstack[TopStack].UnTop,
Unstack[TopStack].UnWidth, Unstack[TopStack].UnHeight);
/// Img.Picture.Graphic:= Unstack[TopStack].UnPicture.Graphic; حذف شود
Unstack[TopStack].UnPicture.Position := 0;
Img.Picture.Bitmap.LoadFromStream(Unstack[TopStack].UnPicture);
TopStack:= TopStack - 1;
end;

end.

reza_king_sh
سه شنبه 14 مهر 1388, 16:59 عصر
مشکل کد شما این هست که بعد از اینکه عکس Image رو تو رکوردتون ریختید اون Image رو Free میکنید .

free می کنم؟؟؟!!!!
مگه چه اشکالی داره من فقط picture اونو لازم دارم که اونم تو رکورد ذخیره می کنم!

reza_king_sh
سه شنبه 14 مهر 1388, 17:24 عصر
یه خورده اشکال درش هست ولی امنیت کاری نداره باید بعدا اصلاحش کنی

ممنون جناب ولی من این کارو قبلاً انجام دادم. البته نه با خاصیت Bitmap. آخه تصویر هایی که تو ایمیج ها قرار می گیرن ممکنه از نوع JPG یا WMF هم باشن!
من برای این کار به جای Bitmap، خاصیت Graphic ایمیجو تو یه MemoryStream ذخیره می کردم. ولی وقتی اونو از Stream لود می کردم، با خطا مواجه می شدم که این سوالو دو سه هفته پیش پرسیدم ولی کسی جواب نداد. حالا دو باره می پرسم.
چطور میشه خاصیت Graphic مربوط به ایمیجی رو که تصویرش از نوع JPG هست تو یه استریم ذخیره کرد و بعد اونو تو یه ایمیج دیگه بازیابی کرد؟؟
من از این کد استفاده می کردم. استریم رو هم از نوع TMemoryStream تعریف می کردم:



Stream:= TMemoryStream.Create;
image1.Picture.Graphic.SaveToStream(Stream);
Stream.Position:= 0;
image2.Picture.Graphic.LoadFromStream(Stream); //اينجا خطا ميگيره
Stream.Position:= 0;

vcldeveloper
سه شنبه 14 مهر 1388, 18:02 عصر
free می کنم؟؟؟!!!!
مگه چه اشکالی داره من فقط picture اونو لازم دارم که اونم تو رکورد ذخیره می کنم!
اشکالش این هست که چیزی که شما در فیلد UnPicture مربوط به رکورد ذخیره می کنید، فقط یک اشاره گر به شی Picture مربوطه هست. اگر شی مربوطه Free بشه، اون اشاره گر هم ارزشی نخواهد داشت، و استفاده از آن باعث ایجاد پیام خطا میشه.

برای اینکه محتوای Picture را نگهداری کنید، می تونید از روش ارائه شده در پست شماره (http://www.barnamenevis.org/forum/showpost.php?p=822379&postcount=3)3 (http://www.barnamenevis.org/forum/showpost.php?p=822379&postcount=3) تاپیک استفاده کنید. البته باید دقت کنید که در پایان کار باید تمامی MemoryStream هایی که به این روش برای نگهداری تصویر ساخته شدند، و در آرایه نگهداری میشند، را Free کنید، وگرنه Memory Leak بوجود میاد.

reza_king_sh
سه شنبه 14 مهر 1388, 21:02 عصر
برای اینکه محتوای Picture را نگهداری کنید، می تونید از روش ارائه شده در پست شماره (http://www.barnamenevis.org/forum/showpost.php?p=822379&postcount=3)3 (http://www.barnamenevis.org/forum/showpost.php?p=822379&postcount=3) تاپیک استفاده کنید. البته باید دقت کنید که در پایان کار باید تمامی MemoryStream هایی که به این روش برای نگهداری تصویر ساخته شدند، و در آرایه نگهداری میشند، را Free کنید، وگرنه Memory Leak بوجود میاد.

خیلی ممنون. ولی من که به مشکلم در این زمینه تو پست شماره 5 گفتم. چرا اونو جواب ندادید؟!

tdkhakpur
سه شنبه 14 مهر 1388, 23:19 عصر
چطور میشه خاصیت Graphic مربوط به ایمیجی رو که تصویرش از نوع JPG هست تو یه استریم ذخیره کرد و بعد اونو تو یه ایمیج دیگه بازیابی کرد؟؟
من از این کد استفاده می کردم. استریم رو هم از نوع TMemoryStream تعریف می کردم:



Stream:= TMemoryStream.Create;
image1.Picture.Graphic.SaveToStream(Stream);
Stream.Position:= 0;
image2.Picture.Graphic.LoadFromStream(Stream); //اينجا خطا ميگيره
Stream.Position:= 0;


بصورت زیر عمل کن



Stream:= TMemoryStream.Create;
Stream.Position:= 0;
image1.Picture.Graphic.SaveToStream(Stream);

Stream.Position:= 0;
image2.Picture.Graphic.Assign(image2.Picture.Bitma p);// این خط را اضافه کن
image2.Picture.Graphic.LoadFromStream(Stream);

vcldeveloper
چهارشنبه 15 مهر 1388, 00:12 صبح
ولی من که به مشکلم در این زمینه تو پست شماره 5 گفتم. چرا اونو جواب ندادید؟! اون پست رو ندیدم.


بصورت زیر عمل کناینطوری یا خطا میگیره، یا تصویر تبدیل به Bitmap میشه.

متن خطا را ننوشتید، ولی احتمالا خطایی که دریافت می کنید بخاطر این هست که خصوصیت Graphic مربوط به Picture همان شی قبلی در زمان ذخیره کردن تصویر نیست.

شیوه کار TPicture به این شکل هست که از روی فایلی که با استفاده از LoadFromFile لود می کنید، نوع تصویر را تشخیص میده، و بر اساس آن، اگر تصویر Bitmap بود، یک شی از TBitmap میسازه که از طریق خصوصیت Bitmap در دسترس هست. اگر آیکن بود، یک شی از TIcon میسازه، اگر پسوندی غیر از اینها بود، به کلاس های رجیستر شده برای Graphic رجوع میکنه، و سعی میکنه کلاسی را که آن نوع تصویر را پشتیبانی می کند، و از TGraphic مشتق شده، پیدا کند، و یک نمونه شی از آن بسازد. شی ایی که به این روش ساخته می شود، از طریق خصوصیت Graphic در دسترس خواهد بود.

حالا با این توضیحات، اگر یک تصویر JPEG در Image در حال نمایش باشه، خصوصیت Picture.Graphic یک شی از کلاس TJpegImage هست. اگر شما با استفاده از Picture.Graphic.SaveToStream داده موجود در آن را ذخیره کنید، یک تصویر JPEG هست. اگر بعد از ذخیره شدن داده، کاربر تصویر دیگه ایی را از نوع دیگه، در Image لود کنه (مثلا یک Bitmap)، اون وقت خصوصیت Picture.Graphic یک شی از کلاس TBitmap خواهد شد، و فقط تصاویر Bitmap را قبول می کند.

شما باید علاوه بر محتوای تصویر، نوع کلاس مشتق شده از TGraphic را هم در رکورد خودتان ذخیره کنید، و اگر نوع تصویر فعلی با نوع تصویر قبلی تفاوت داشت، یک شی جدید ایجاد کنید و به خصوصیت Graphic مربوطه اختصاص بدید. به عنوان مثال:

در کد زیر فرض شده که رکورد شما یک فیلد با نام UnGraphicClass از نوع TClass دارد. برای مقداردهی به این فیلد باید از کدی مثل این استفاده کنید:

UnStack[TopStack].UnGraphicClass := Image2.Picture.Graphic.ClassType;و برای اختصاص دادن تصویر موجود در رکورد به Image باید از کدی شبیه به این استفاده کنید:

var
NewGraphic : TGraphic;
begin
if Assigned(Image2.Picture.Graphic) then
begin
if Image2.Picture.Graphic.ClassType <> UnStack[TopStack].UnGraphicClass then
begin
NewGraphic := UnStack[TopStack].UnGraphicClass.Create;
NewGraphic.LoadFromStream(UnStack[TopStack].UnPicture);
Image2.Picture.Graphic := NewGraphic;
end
else
Image2.Picture.Graphic.LoadFromStream(UnStack[TopStack].UnPicture);
end;
end;دقت کنید که من این کد را دلفی تست نکردم، و ممکن هست مشکل داشته باشه. در هر حال، کلیت کار به این شکلی هست که در اینجا توضیح دادم.

tdkhakpur
چهارشنبه 15 مهر 1388, 13:19 عصر
اینطوری یا خطا میگیره، یا تصویر تبدیل به Bitmap میشه.

خوب آقای کشاورز حطا که نمیگیره چون با این کار خصوصیات آن استفاده خواهد شد و همچنین قرار نیست که تبدیلات صورت نگیرد.
ولی در مورد اینکه شما شرط را بصورت زیر اعلام کردید


if Assigned(Image2.Picture.Graphic) then

خوب آقای reza منظورشان اینه که نمیداند کدام یک از پالتها و یا فرمتهای تصویری میخواد لود بشود به هر حال موارد ارسالی شما قسمتی از حل مساله هست نه حالت کلی مساله.

vcldeveloper
چهارشنبه 15 مهر 1388, 14:52 عصر
خوب آقای کشاورز حطا که نمیگیره چون با این کار خصوصیات آن استفاده خواهد شد و همچنین قرار نیست که تبدیلات صورت نگیرد.
در پست قبل توضیح دادم، اگر تصویر ذخیره شده در رکورد فرضا Bitmap باشه، و تصویر فعلی JPEG باشه، خصوصیت Picture.Graphic حاوی یک نمونه شی از کلاس TJpegImage خواهد بود، در حالی که تصویر ذخیره شده در رکورد شما Bitmap هست. حالا اگر این Bitmap را به اون نمونه شی از TJpegImage اختصاص بدید، چون Type آنها یکی نیست، تابع Assign خطا برمیگردونه.


خوب آقای reza منظورشان اینه که نمیداند کدام یک از پالتها و یا فرمتهای تصویری میخواد لود بشود به هر حال موارد ارسالی شما قسمتی از حل مساله هست نه حالت کلی مساله.
اگر پست قبلی را با دقت مطالعه کنید، توضیح داده شده که باید به همراه تصویر، نوع کلاس مرتبط با آن هم در رکورد ذخیره شود، تا بتوان در صورت نیاز، از روی آن نمونه شی سازگار با تصویر ذخیره شده را ایجاد کرد. برای این کار یک نمونه سورس کد هم ارسال شده.

اون شرط if Assigned(Image2.Picture.Graphic( هم ربطی به سوال این بنده خدا نداره. اون شرط برای این در کد قرار داده شده که اگر Image حاوی هیچ تصویری نبود، با اجرای آن سورس، کاربر Access Violation دریافت نکنه. چون در صورتی که Image2 حاوی تصویر نباشه، خصوصیت Picture.Graphic = nil خواهد بود.

reza_king_sh
پنج شنبه 16 مهر 1388, 23:50 عصر
از دو دوست عزیز ممنون.
من مشکلم حل شد.
از روشی که tdkhakpur گفته بودن استفاده نکردم چون فرمت تصویر ها مهم بود که عوض نشه!
همون طور که آقای کشاورز گفتن من کلاس عکس رو هم ذخیره کردم و بعد در هنگام ایجاد تصویر، اول از همون نوع کلاس یه دونه ایجاد کردم و بعد اونو انتساب دادمم به خاصیت گرافیک مربوط به اون ایمیج. بعد هم تصویر رو از تو ستریم لود کردم داخل خاصیت گرافیکش.
از اون شرط هم استفاده نکردم چون وقتی کاربر در برنامه یه ایمیج ایجاد می کنه من یه تصویر پیش فرض توش لود میکنم که نماد خالی بودن ایمیجه.
با سپاس فراوان از هر دوی شما.