PDA

View Full Version : سوال: کار با Stream



rezamahdizadeh
یک شنبه 19 دی 1389, 13:54 عصر
کار Position از stream چیست؟
فرض کنیم 1000 بایت به ابتدای یک فایل jpg اضافه کرده ایم و می خواهیم کل این داده ها را در یک شی از نوع TMemoryStream لود کنیم و داده های موجود در این متغیر را از بایت 1001 را به ورودی یک TImage بدهیم. چکار کنیم؟

vcldeveloper
یک شنبه 19 دی 1389, 15:09 عصر
سوال درباره stream ها به تالار کامپوننت ها مربوط نمیشه.


کار Position از stream چیست؟
همون معنی لغوی اسمش؛ مکان اشاره گر در داخل stream رو مشخص میکنه.


فرض کنیم 1000 بایت به ابتدای یک فایل jpg اضافه کرده ایم و می خواهیم کل این داده ها را در یک شی از نوع TMemoryStream لود کنیم و داده های موجود در این متغیر را از بایت 1001 را به ورودی یک TImage بدهیم. چکار کنیم؟
اشاره گر stream رو بر روی 1001 تنظیم می کنید، یک stream دیگه Create می کنید، و با استفاده از متد CreateFrom اون Stream دوم، مقدار Stream اول رو از محل اشاره گر تا انتهای Stream در Stream دوم می ریزید. این میشه داده JPEG شما.
برای لود کردنش در TImage، باید یک نمونه شی از TJpegImage بسازید، و این Stream رو درش لود کنید. سپس این شی TJpegImage رو Assign کنید به خصوصیت Picture مربوط به شی TImage مربوطه.


uses jpeg;

procedure ExtractJPegImage(Source: TStream; out JpegImage: TJPEGImage);
var
DestStream : TMemoryStream;
begin
if (not Assigned(Source)) or (not Assigned(JpegImage)) then
raise Exception.Create('Invalid parameter');

DestStream := TMemoryStream.Create;
try
Source.Position := 1001;
DestStream.CopyFrom(Source,Source.Size - Source.Position);
JpegImage.LoadFromStream(DestStream);
finally
DestStream.Free;
end;
end;

procedure ExtractImageFromStream(Source: TStream; Image: TImage);
var
JpegImage : TJPEGImage;
begin
JpegImage := TJPEGImage.Create;
try
ExtractJPegImage(Source,JpegImage);
Image.Picture.Assign(JpegImage);
finally
JpegImage.Free;
end;
end;

نحوه استفاده اش هم میشه:


var
SourceStream : TMemoryStream;
begin
SourceStream := TMemoryStream.Create;
try
SourceStream.LoadFromFile('c:\somefile');
ExtractImageFromStream(SourceStream, Image1);
finally
SourceStream.Free;
end;
end;

rezamahdizadeh
یک شنبه 19 دی 1389, 17:42 عصر
در بخش مباحث عمومی دلفی و پاسکال خطای زیر پیش آمد اما در بخش کامپوننت های سایر شرکت ها و توسعه کامپوننت خطایی رخ نداد.

شما وارد نشده اید و یا اجازه مشاهده این صفحه را ندارید. این موضوع می تواند به خاطر موارد زیر باشد:


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

vcldeveloper
یک شنبه 19 دی 1389, 22:18 عصر
شما وارد نشده اید و یا اجازه مشاهده این صفحه را ندارید. این موضوع می تواند به خاطر موارد زیر باشد:
این خطا مربوط به زیر گروه مباحث عمومی دلفی و پاسکال نیست، بلکه شما سعی کردید در تالار اصلی دلفی تاپیک ایجاد کنید، و این خطا را اونجا دریافت کردید. کاربران اجازه ایجاد تاپیک در تالار اصلی دلفی را ندارند، و باید تاپیک های خود را در یکی از زیر گروه های تالار دلفی مطرح کنند. زیرگروه مناسب سوال شما هم مباحث عمومی دلفی و پاسکال بود.

rezamahdizadeh
سه شنبه 28 دی 1389, 14:46 عصر
اگر به جای فایل عکس یک فایل بزرگ باشد اجرای دستور زیر خیلی زمانگیر می شود آیا برای رفع این مشگل راه حلی وجود دارد؟ MemoryStream در RAM است یا HDD؟
DestStream.CopyFrom(Source,Source.Size - Source.Position);

vcldeveloper
سه شنبه 28 دی 1389, 18:08 عصر
MemoryStream در RAM است یا HDD؟
در اسمش مشخص هست؛ TMemoryStream کل داده Stream را در RAM لود میکنه.


اگر به جای فایل عکس یک فایل بزرگ باشد اجرای دستور زیر خیلی زمانگیر می شود آیا برای رفع این مشگل راه حلی وجود دارد؟
بستگی داره؛ طبیعی هست که حجم بالای داده نیاز به زمان بالایی برای کپی شدن داشته باشه. البته می تونید یک کلاس جدید از TCustomMemoryStream خودتون مشتق کنید، که به جای کپی داده از یک Stream به Stream دیگه، اشاره گر حافظه خودش را روی حافظه Stream دیگه ایی تنظیم کنه، مثلا اشاره گر خودش را روی بایت 100 از حافظه Stream ایی که نقش منبع داره، تنظیم کنه. اینطوری از یک فضای حافظه برای هر دو Stream استفاده میشه. البته باید دقت کنید که در زمان استفاده از این Stream، نباید Stream اول آزاد بشه، چون آزاد شدن آن به معنای از بین رفتن حافظه در حال استفاده Stream شما ست. برای تغییر آدرس حافظه در Stream خودتان باید از متد SetPointer کلاس TCustomMemoryStream استفاده کنید.

اگر فقط میخواید که کپی داده موجب فریز شدن برنامه تان نشه، می تونید اون کار رو در یک Thread مستقل انجام بدید، یا داده ها را به صورت Chunk کپی کنید، نه اینکه همه را یکجا کپی کنید.

ashoori
پنج شنبه 30 دی 1389, 10:29 صبح
علی آقا مطلب خیلی جالبی رو اشاره فرمودید، حالا امکان داره یه نمونه کوچک رو بنویسید!
باتشکر

vcldeveloper
پنج شنبه 30 دی 1389, 12:23 عصر
حالا امکان داره یه نمونه کوچک رو بنویسید!
الان فرصتش نیست، ولی کار پیچیده ایی نیست. یک کلاس جدید از TCustomMemoryStream می نویسید، متد SetPointer اش را به جای بخش protected، در بخش public تعریف می کنید. حالا می تونید اشاره گر حافظه اش را با استفاده از این متد تغییر بدید. یک TMemoryStream یک خصوصیت به نام Memory داره که اشاره گر به حافظه داخلی آن هست. شما از Stream مبداء خودتان این اشاره گر را می گیرید، و در Stream مقصد، با استفاده از متد SetPointer، اشاره گر حافظه را روی مقدار برگشتی از خصوصیت Memory از Stream مبداء تنظیم می کنید. اینطوری هر دو Stream به یک حافظه اشاره می کنند.

rezamahdizadeh
شنبه 23 بهمن 1389, 17:23 عصر
می شود کد مربوط به آن را بنویسید

rezamahdizadeh
یک شنبه 04 اردیبهشت 1390, 17:01 عصر
برای تغییر آدرس حافظه در Stream خودتان باید از متد SetPointer کلاس TCustomMemoryStream استفاده کنید.
در اجرای دستور زیر به مشگل برخوردم

SetPointer(Ptr+Offset, Size-Offset);
خطای زیر رخ می دهد:
Operator not applicable to this operand type
با این توضیح که Ptr از نوع Pointer و Offset از نوع Integer است. ptr آدرس MemoryStream اول است و می خواهیم اشاره گر MemoryStream دوم به Offset بایت پس از Ptr اشاره کند.
چه کار کنیم که بتوانیم این هدف برآورده شود؟

vcldeveloper
یک شنبه 04 اردیبهشت 1390, 17:37 عصر
خطای زیر رخ می دهد:
Operator not applicable to this operand type
با این توضیح که Ptr از نوع Pointer و Offset از نوع Integer است.
Pointer Math به طور پیش فرض در دلفی غیر فعال هست. می تونید مقدار اون اشاره گر رو ابتدا به NativeInt تبدیل نوع (Type cast) کنید، سپس جمع مورد نظرتون رو انجام بدید، و نتیجه را مجددا به یک اشاره گر Type cast کنید:


var
iAddress : NativeInt;
Offset : NativeInt;
Ptr : Pointer;
begin
iAddress := NativeInt(Ptr) + Offset;
Ptr := Pointer(iAddress);
end;