CYFROWY BARON • PROGRAMOWANIE • Zobacz wątek - Gdiplus::Image - Zapis do pliku

Gdiplus::Image - Zapis do pliku

problemy z tworzeniem aplikacji graficznych oraz audio i wideo

Gdiplus::Image - Zapis do pliku

Nowy postprzez Mironas » czwartek, 27 września 2012, 12:06

Witam,

Obiekt Gdiplus::Image* img wczytany (utworzony) z pliku.

Wykonuję na nim jakieś zmiany i próbuję zapisać je do TEGO SAMEGO pliku.
image->Save(...)
Zwraca błąd 'Win32Error'.
Jak się domyślam plik jest otwarty tylko do odczytu i dopóki nie zniszczę img to nie mogę do niego pisać. Ale niszczenie img przed zapisem jest bez sensu.

Jedyne co wymyśliłem to utworzyć bitmapę (bmp) z kopią img, zniszczyć img i zapisać bmp do pliku (przykład poniżej). Jednak musiałbym też przepisać wszystkie metadane (zależy mi na nich) a to jeszcze bardziej skomplikuje zapis.

Czy jest prostszy sposób na zapisanie img do tego samego pliku z którego został utworzony?

Przykład zapisu z wykorzystaniem bitmapy:
KOD cpp:     UKRYJ  
void __fastcall TForm1::ToolButton1Click(TObject *Sender)
{
  String plik = "C:\\Plik.jpg";
  Gdiplus::Status status;
  CLSID clsid;

  Gdiplus::Image* img = new Gdiplus::Image(plik.w_str());

  // tu wykonuję modyfikacje na 'image1'

  // przepisanie img >> bmp
  Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(img->GetWidth(), img->GetHeight(), PixelFormat32bppARGB);
  GGraphics graf (bmp);
  graf.DrawImage(img, 0, 0);

  // niszczenie img
  delete img;

  // zapis do pliku
  GetEncoderClsid(L"image/jpeg", &clsid);
  status = bmp->Save(plik.w_str(), &clsid);
  if (status)
    Beep();

  delete bmp;
}
 

PS
Utworzenie nowego obiektu przez Gdiplus::Image* img2 = img->Clone() i zniszczenie img nic nie daje ponieważ nie da się pisać do pliku zanim nie usunie się img2 :( .
Avatar użytkownika
Mironas
Programista I
Programista I
 
Posty: 446
Dołączył(a): poniedziałek, 2 stycznia 2012, 19:02
Podziękował : 21
Otrzymał podziękowań: 63
System operacyjny: Windows 10
Kompilator: C++Builder 10.2 Tokyo
TMS Components Pack
Gadu Gadu: 0
    Windows XPChrome

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Cyfrowy Baron » czwartek, 27 września 2012, 16:52

Nie wiem co robisz źle, ale u mnie to działa:

Plik nagłówkowy np. Unit1.h
KOD cpp:     UKRYJ  
#include "gdiplus.h"
private:
                Gdiplus::GdiplusStartupInput gdiplusStartupInput;
                ULONG_PTR gdiplusToken;


Plik źródłowy np. Unit1.cpp
KOD cpp:     UKRYJ  
#pragma link "gdiplus.lib"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
}
//---------------------------------------------------------------------------
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
 unsigned int num = 0;
 unsigned int size = 0;

 Gdiplus::GetImageEncodersSize(&num, &size);
 if(size == 0)return -1;

 Gdiplus::ImageCodecInfo* imageCodecInfo = new Gdiplus::ImageCodecInfo[size];
 Gdiplus::GetImageEncoders(num, size, imageCodecInfo);

 for(unsigned int i = 0; i < num; ++i)
 {
  if(wcscmp(imageCodecInfo[i].MimeType, format) == 0)
  {
   *pClsid = imageCodecInfo[i].Clsid;
   delete[] imageCodecInfo;
   return i;
  }
 }
 delete[] imageCodecInfo;
 return -1;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button11Click(TObject *Sender)
{
 Gdiplus::Graphics graphics(this->Handle);
 String sFile = "c:\\plik.jpg";
 Gdiplus::Image imageFile( sFile.c_str(), false );

 /* Modyfikacja obrazu - obrócenie o 90 stopni */
 imageFile.RotateFlip(Gdiplus::Rotate90FlipNone);

 CLSID fClsid;
 GetEncoderClsid(L"image/jpeg", &fClsid);

 imageFile. Save(sFile.c_str(), &fClsid, NULL);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 Gdiplus::GdiplusShutdown(gdiplusToken);
}


Dlaczego tak:

Mironas napisał(a):
KOD cpp:     UKRYJ  
Gdiplus::Image* img = new Gdiplus::Image(plik.w_str());


Skoro to nie jest obiekt globalny lecz lokalny?
Oczywiście z obiektem prywatnym lub publicznym też działa prawidłowo:

Plik nagłówkowy np. Unit1.h
KOD cpp:     UKRYJ  
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;

        Gdiplus::Image *imageFile;


Plik źródłowy np. Unit1.cpp
KOD cpp:     UKRYJ  
void __fastcall TForm1::Button11Click(TObject *Sender)
{
 Gdiplus::Graphics graphics(this->Handle);
 String sFile = "c:\\plik.jpg";
 imageFile = new Gdiplus::Image( sFile.c_str() );

 /* Modyfikacja obrazu - obrócenie o 90 stopni */
 imageFile->RotateFlip(Gdiplus::Rotate90FlipNone);

 CLSID fClsid;
 GetEncoderClsid(L"image/jpeg", &fClsid);

 imageFile->Save(sFile.c_str(), &fClsid, NULL);
}


Wypróbuj ten kod. Jeżeli u Ciebie nie zadziała to będzie oznaczać, że to wina bibliotek środowiska XE.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4727
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Mironas » czwartek, 27 września 2012, 18:42

Faktycznie - twój przykład u mnie działa, ale wystarczy usunąć RotateFlip i już nie chce się zapisać. Oczywiście nie ma sensu zapisywać niezmodyfikowanego pliku, ale w moim przypadku modyfikacje polegają zmianie metadanych. A niestety po takiej zmianie nadal nie chce się zapisać.
Przykład:
KOD cpp:     UKRYJ  
void __fastcall TForm1::ToolButton2Click(TObject *Sender)
{
  String sFile = "C:\\Plik01.png";
  Gdiplus::Image imageFile(sFile.c_str());

  // dodaj metadane
  AnsiString sKomentarz = "Ala ma kota.";
  Gdiplus::PropertyItem* item = new Gdiplus::PropertyItem();
  item->id = PropertyTagExifUserComment;
  item->length = sKomentarz.Length()+1;         // length + null_terminator
  item->type = PropertyTagTypeASCII;
  item->value = sKomentarz.c_str();
  imageFile.SetPropertyItem(item);
  delete item;

  CLSID fClsid;
  GetEncoderClsid(L"image/png", &fClsid);

  //sFile = "C:\\Plik02.png";   // zapisanie pod inną nazwą
  Gdiplus::Status status = imageFile.Save(sFile.c_str(), &fClsid);
  if ( status )
    Beep();
}
 


Procedura zapisania komentarza do pliku działa poprawnie na plikach PNG co można sprawdzić zapisując plik pod inną nazwą i odczytując zapisany komentarz taką funkcją:
KOD cpp:     UKRYJ  
void __fastcall TForm1::ToolButton4Click(TObject *Sender)
{
  String sFile = "C:\\Plik02.png";
  Gdiplus::Image imageFile(sFile.c_str());

  // odczyt komentarza
  UINT size = imageFile.GetPropertyItemSize(PropertyTagExifUserComment);
  if ( size )
  {
    Gdiplus::PropertyItem* item = (Gdiplus::PropertyItem*)malloc(size);
    imageFile.GetPropertyItem(PropertyTagExifUserComment, size, item);
    AnsiString sKomentarz = (char*)item->value;
    ShowMessage(sKomentarz);
    free(item);
  }
}
 

UWAGA - zapisywanie i odczytywanie komentarza tymi funkcjami nie działa u mnie poprawnie na plikach JPG, ale na moje potrzeby wystarczy PNG.

Dlaczego tak:
Gdiplus::Image* img = new Gdiplus::Image(plik.w_str());
Skoro to nie jest obiekt globalny lecz lokalny?

Wiem. U mnie w programie IMG jest globalne, i tylko tak szybko poskładałem aby zrobić jedną funkcję demonstrującą problem.
Avatar użytkownika
Mironas
Programista I
Programista I
 
Posty: 446
Dołączył(a): poniedziałek, 2 stycznia 2012, 19:02
Podziękował : 21
Otrzymał podziękowań: 63
System operacyjny: Windows 10
Kompilator: C++Builder 10.2 Tokyo
TMS Components Pack
Gadu Gadu: 0
    Windows XPChrome

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Cyfrowy Baron » czwartek, 27 września 2012, 19:50

Nie wiem czemu, ale jeżeli nie zmodyfikujesz zawartości pliku, nie da się go zapisać, ale można to rozwiązać bez konieczności przerysowywania grafiki:

KOD cpp:     UKRYJ  
void __fastcall TForm1::Button11Click(TObject *Sender)
{
 Gdiplus::Status status;
 Gdiplus::EncoderParameters encoderParameters;
 CLSID fClsid;
 ULONG quality;


 String sFile = "c:\\plik.jpg";
 String sTemp = "c:\\~plik.jpg";

 if(imageFile == NULL && FileExists(sFile) )
  imageFile = new Gdiplus::Image( sFile.c_str(), FALSE );

 AnsiString sKomentarz = "Ala ma kota.";
 Gdiplus::PropertyItem* item = new Gdiplus::PropertyItem();
 item->id = PropertyTagExifUserComment;
 item->length = sKomentarz.Length()+1;         // length + null_terminator
 item->type = PropertyTagTypeASCII;
 item->value = sKomentarz.c_str();
 imageFile->SetPropertyItem(item);
 delete item;

 GetEncoderClsid(L"image/jpeg", &fClsid);

 /* przykład ustawiania stopnia kompresji pliku */
 encoderParameters.Count = 1;
 encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
 encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
 encoderParameters.Parameter[0].NumberOfValues = 1;
 quality = 100;
 encoderParameters.Parameter[0].Value = &quality;
 /* koniec przykładu */

 status = imageFile->Save( sTemp.c_str(), &fClsid, &encoderParameters); /* zapisywanie do pliku tymczasowego */

 if (status)
 {
        Beep();
 }

 delete imageFile;

 if(!DeleteFile(sFile)) ShowMessage("Nie można usunąć pliku!"); /* usuwanie oryginalnego pliku */
 if(!RenameFile(sTemp, sFile)) ShowMessage("Błąd"); /* zmiana nazwy pliku tymczasowego na oryginalny */
}


Będę szukał, ale póki co to jest chyba najlepsze rozwiązanie. :roll:

Za ten post autor Cyfrowy Baron otrzymał podziękowanie od:
Mironas
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4727
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Mironas » piątek, 28 września 2012, 09:26

Dzięki za pomysł. Jest to jakieś rozwiązanie chyba szybsze w działaniu niż obracanie zawartości.
Chociaż nadal nie rozumiem dlaczego nie mogę po prostu zapisać pliku. Może ktoś ma pomysł jak oszukać IMAGE że niby jest zmieniony?
Avatar użytkownika
Mironas
Programista I
Programista I
 
Posty: 446
Dołączył(a): poniedziałek, 2 stycznia 2012, 19:02
Podziękował : 21
Otrzymał podziękowań: 63
System operacyjny: Windows 10
Kompilator: C++Builder 10.2 Tokyo
TMS Components Pack
Gadu Gadu: 0
    Windows XPChrome

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez polymorphism » piątek, 28 września 2012, 09:52

Zwraca błąd 'Win32Error'.

Sprawdź, co zwraca funkcja GetLastError po wystąpieniu tego błędu.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2187
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 204
System operacyjny: Windows 7 Pro
Windows 8.1
Linux Mint 19
Kompilator: Visual Studio 2015 Community
CodeLite (MinGW)
MSYS2 (MinGW, clang)
g++
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Mironas » piątek, 28 września 2012, 10:13

32
ERROR_SHARING_VIOLATION
Proces nie może uzyskać dostępu do pliku, ponieważ jest on używany przez inny proces.
Avatar użytkownika
Mironas
Programista I
Programista I
 
Posty: 446
Dołączył(a): poniedziałek, 2 stycznia 2012, 19:02
Podziękował : 21
Otrzymał podziękowań: 63
System operacyjny: Windows 10
Kompilator: C++Builder 10.2 Tokyo
TMS Components Pack
Gadu Gadu: 0
    Windows XPChrome

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez polymorphism » piątek, 28 września 2012, 11:15

W dokumentacji masz jak wół napisane:

    GDI+ does not allow you to save an image to the same file that you used to construct the image. The following code creates an Image object by passing the file name MyImage.jpg to an Image constructor. That same file name is passed to the Image::Save method of the Image object, so the Image::Save method fails.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2187
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 204
System operacyjny: Windows 7 Pro
Windows 8.1
Linux Mint 19
Kompilator: Visual Studio 2015 Community
CodeLite (MinGW)
MSYS2 (MinGW, clang)
g++
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Mironas » piątek, 28 września 2012, 11:33

No właśnie szukamy sposobu aby to ominąć i jednak zapisać (najmniej kosztownym sposobem) obraz do tego samego pliku.
Avatar użytkownika
Mironas
Programista I
Programista I
 
Posty: 446
Dołączył(a): poniedziałek, 2 stycznia 2012, 19:02
Podziękował : 21
Otrzymał podziękowań: 63
System operacyjny: Windows 10
Kompilator: C++Builder 10.2 Tokyo
TMS Components Pack
Gadu Gadu: 0
    Windows XPChrome

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez polymorphism » piątek, 28 września 2012, 12:24

Obawiam się, że bez robienia kopii obrazka się nie obejdzie. Spróbuj tak:
  1. załaduj obrazek do Image.
  2. zmień co tam masz zmienić.
  3. zapisz bitmapę z Image'a do strumienia pamięciowego (IStream).
  4. usuń obiekt Image.
  5. zapisz strumień pamięciowy do pliku.

Choć prościej by było po prostu zapisać obrazek do pliku pod inna nazwą, usunąć oryginalny plik i zmienić nazwę kopii na nazwę oryginału.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2187
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 204
System operacyjny: Windows 7 Pro
Windows 8.1
Linux Mint 19
Kompilator: Visual Studio 2015 Community
CodeLite (MinGW)
MSYS2 (MinGW, clang)
g++
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Cyfrowy Baron » piątek, 28 września 2012, 18:37

polymorphism napisał(a):Spróbuj tak:


Pkt. 3 i 5 są zbędne skoro można zapisać plik do innego pliku, potem usunąć oryginalny i zmienić nazwę pliku. To chyba będzie szybsze niż zabawa ze strumieniem?
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4727
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez polymorphism » piątek, 28 września 2012, 20:21

No tak, w ostatnim zdaniu napisałem, że opcja z zapisywaniem od razu do pliku jest prostsza. A czy szybsza? Być może.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2187
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 204
System operacyjny: Windows 7 Pro
Windows 8.1
Linux Mint 19
Kompilator: Visual Studio 2015 Community
CodeLite (MinGW)
MSYS2 (MinGW, clang)
g++
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Cyfrowy Baron » sobota, 29 września 2012, 09:21

Sprawdzałem, przeglądałem fora i nie da się tego obejść.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4727
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Mironas » sobota, 29 września 2012, 15:15

Mi najbardziej pasuje zapisanie IMAGE do innego pliku, usunięcie IMAGE, usunięcie oryginalnego pliku i zmiana nazwy zapisanego pliku. Jedyne
dodatkowe czynności to kasowanie pliku i zmiana nazwy pliku. Nie trzeba tworzyć dodatkowych obiektów IMAGE/BITMAP ani strumieni, co przy dużych rozmiarach będzie pamięcio- i czasochłonne.

Przy małych, pojedynczych plikach można też zrobić podwójne RotateFlip obracając obraz np 2 razy w pionie. Prostsze do wykonania.

Dzięki za zaangażowanie i za wszystkie uwagi, sugestie i pomysły.
Avatar użytkownika
Mironas
Programista I
Programista I
 
Posty: 446
Dołączył(a): poniedziałek, 2 stycznia 2012, 19:02
Podziękował : 21
Otrzymał podziękowań: 63
System operacyjny: Windows 10
Kompilator: C++Builder 10.2 Tokyo
TMS Components Pack
Gadu Gadu: 0
    Windows XPChrome

Re: Gdiplus::Image - Zapis do pliku

Nowy postprzez Cyfrowy Baron » sobota, 29 września 2012, 15:25

Mironas napisał(a):Przy małych, pojedynczych plikach można też zrobić podwójne RotateFlip obracając obraz np 2 razy w pionie. Prostsze do wykonania.


Tego nie polecam, gdyż obrazek ulegnie modyfikacji.

Sugerowałbym poszukanie innego sposobu na zapisywanie tagów do plików graficznych.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4727
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 444
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Następna strona

  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Aplikacje multimedialne, graficzne

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 1 gość

cron