Сохранение битмапа - кто короче ?
От: Hollander Беларусь http://blogs.rsdn.org/ikemefula
Дата: 27.11.01 16:35
Оценка: 9 (3)
Привет !

Встретил топик похожий, налабал решение попроще. Так можно сохранять хучь битмапы, хучь иконки, хучь метафайлы и вообще все объекты, поддерживающие IPicture. Написал на Cи — так круче.


BOOL SaveBitmap(HBITMAP hBitmap,HPALETTE hPal,LPCTSTR szPath)
{
    PICTDESC pDesc;
    
    pDesc.cbSizeofstruct = sizeof(PICTDESC);
    pDesc.bmp.hbitmap = hBitmap;
    pDesc.bmp.hpal = hPal;
    pDesc.picType =  PICTYPE_BITMAP;

    return SavePictureIndirect(pDesc,szPath);
}

BOOL SavePictureIndirect(PICTDESC pDesc,LPCTSTR szPath)
{
    IPicture* pPicture = NULL;
    BOOL bResult = TRUE;

    OleCreatePictureIndirect(&pDesc,&IID_IPicture,FALSE,(void**)&pPicture);
    if(pPicture == NULL)
        bResult = FALSE;
    else
    {
        pPicture->lpVtbl->AddRef(pPicture);
        bResult = SavePicture(pPicture,szPath);
    }
    if(pPicture != NULL)
        pPicture->lpVtbl->Release(pPicture);
    return bResult;
}

BOOL SavePicture(IPicture* pPicture,LPCTSTR szPath)
{
    IStream* pStream = NULL;
    HGLOBAL hgl = NULL;
    void* lpVoid = NULL; 
    long nSize = 0;
    HRESULT hr = S_OK;
    HANDLE handle = NULL;
    LONG res = 0;
    DWORD err = 0;
    

    if(pPicture == NULL)
        return FALSE;
    
    do
    {
        hgl = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,8192);
        if(hgl == NULL)
            break;
        CreateStreamOnHGlobal(hgl,FALSE,&pStream);
        if(pStream == NULL)
            break;
        pPicture->lpVtbl->put_KeepOriginalFormat(pPicture,TRUE);
        hr = pPicture->lpVtbl->SaveAsFile(pPicture,pStream,TRUE,&nSize);
        if(FAILED(hr))
            break;
        handle = CreateFile(szPath,
            GENERIC_WRITE,
            FILE_SHARE_WRITE,NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
        lpVoid = GlobalLock(hgl);
        WriteFile(handle,lpVoid,nSize,&res,NULL);
    }
    while(FALSE);
    if(pPicture != NULL)
        pPicture->lpVtbl->Release(pPicture);
    if(pStream != NULL)
        pStream->lpVtbl->Release(pStream);
    if(hgl != NULL)
    {
        if(lpVoid != NULL)
            GlobalUnlock(hgl);
        GlobalFree(hgl);
    }
    if(handle != NULL)
        CloseHandle(handle);
    return (res != 0) && (res == nSize);
}
Re: Сохранение битмапа - кто короче ?
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.11.01 16:59
Оценка:
Здравствуйте Hollander, Вы писали:


Посмотри (в MSDN) IPictureDisp (IPicture) и функции с ними связанные.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Сохранение битмапа - кто короче ?
От: Hollander Беларусь http://blogs.rsdn.org/ikemefula
Дата: 27.11.01 17:09
Оценка:
Здравствуйте VladD2, Вы писали:

VD>Посмотри (в MSDN) IPictureDisp (IPicture) и функции с ними связанные.


Экий ты торопыга,а чем же я и пользовался ? Посмотри код, или ты COM только на C++ понимаешь ?
Re[3]: Сохранение битмапа - кто короче ?
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.11.01 20:17
Оценка:
Здравствуйте Hollander, Вы писали:

VD>>Посмотри (в MSDN) IPictureDisp (IPicture) и функции с ними связанные.


H>Экий ты торопыга,а чем же я и пользовался ? Посмотри код, или ты COM только на C++ понимаешь ?


Сори увидел разные "PICTDESC pDesc;" и решил, что ты нас АПИшнинкой решил удивить.

Слушай, а зачем ты так:
pPicture->lpVtbl->AddRef(pPicture);

? А?

Да, и зачем делать AddRef перед передачей параметра в функцию?

Делать же Release переданного в функцию объекта вообще нельзя (по правилам COM).

И HRESULT проверять немешало бы.

А короче, можно... испльзуем смартпоинтеры и библиотечные реализации стрима над файлом и будет короче.

PS

Ты ы малость причисал этот код и можно было бы его в журнал (RSDN Mag) сунуть как FAQ.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Сохранение битмапа - кто короче ?
От: IT Россия linq2db.com
Дата: 27.11.01 20:28
Оценка:
Здравствуйте VladD2, Вы писали:

VD>Ты ы малость причисал этот код и можно было бы его в журнал (RSDN Mag) сунуть как FAQ.


Точно, но лучше бы убрать мазахизм в виде "Написал на Cи — так круче" (C) Hollander
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: Сохранение битмапа - кто короче ?
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.11.01 20:36
Оценка:
Здравствуйте IT, Вы писали:

IT>Здравствуйте VladD2, Вы писали:


VD>>Ты ы малость причисал этот код и можно было бы его в журнал (RSDN Mag) сунуть как FAQ.


IT>Точно, но лучше бы убрать мазахизм в виде "Написал на Cи — так круче" (C) Hollander


Господи, это же на C! Во блин не узнал. О дожил!

Не ну COM на С это уже перебор. Я конечно понимаю, что это круто, но... ооо... ууу...

Но все равно программирование COM-а на C не снимает ответственности за правильную работу с AddRef/Release.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Сохранение битмапа - кто короче ?
От: Hollander Беларусь http://blogs.rsdn.org/ikemefula
Дата: 27.11.01 21:04
Оценка:
Здравствуйте VladD2, Вы писали:

VD>Сори увидел разные "PICTDESC pDesc;" и решил, что ты нас АПИшнинкой решил удивить.


VD>Слушай, а зачем ты так:

VD>
pPicture->>lpVtbl->AddRef(pPicture);
VD>

VD>?
Можно и так : IPicture_Release(pPicture);


VD>Да, и зачем делать AddRef перед передачей параметра в функцию?


Там этот указатель используется — по все правилам COM.

VD>Делать же Release переданного в функцию объекта вообще нельзя (по правилам COM).

Так я и не делаю — это освобождается после OleCreatePictureIndirect, а в функции свой Release есть еще.


VD>И HRESULT проверять немешало бы.


Это не нужно — я проверяю на NULL.

VD>А короче, можно... испльзуем смартпоинтеры и библиотечные реализации стрима над файлом и будет короче.


Я взял чистый API — смартпоинтеры и библиотечные реализации стрима — по колву кода будет длиннее.

VD>PS


VD>Ты ы малость причисал этот код и можно было бы его в журнал (RSDN Mag) сунуть как FAQ.


А поподробнее можно ?
Re[5]: Сохранение битмапа - кто короче ?
От: Alex Fedotov США  
Дата: 27.11.01 21:27
Оценка:
Здравствуйте Hollander, Вы писали:

VD>>Да, и зачем делать AddRef перед передачей параметра в функцию?


H>Там этот указатель используется — по все правилам COM.


Это не по правилам COM.

VD>>Делать же Release переданного в функцию объекта вообще нельзя (по правилам COM).

H>Так я и не делаю — это освобождается после OleCreatePictureIndirect, а в функции свой Release есть еще.

VD>>И HRESULT проверять немешало бы.


H>Это не нужно — я проверяю на NULL.


А вот это не по правилам COM.

По правилам COM будет

    IPicture* pPicture = NULL;
    HRESULT hRes;

    hRes = OleCreatePictureIndirect(&pDesc,&IID_IPicture,FALSE,(void**)&pPicture);
    if (SUCCEEDED(hRes))
    {
        // pPicture->Release из SavePicture убрать!!!
        // возвращаемое значение сделать HRESULT
        hRes = SavePicture(pPicture,szPath);
        pPicture->lpVtbl->Release(pPicture);
    }
    return hRes;
-- Alex Fedotov
Re[5]: Сохранение битмапа - кто короче ?
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.11.01 21:46
Оценка:
Здравствуйте Hollander, Вы писали:

H>Можно и так : IPicture_Release(pPicture);


А разве нет универсального макроса?

Да я по началу не въехал, что код на C. Сори. Привычка блин. COM == С++/VB/Delphi. Короче, стереотип.

VD>>Да, и зачем делать AddRef перед передачей параметра в функцию?


H>Там этот указатель используется — по все правилам COM.


Не-аа! Против правил.

По правилам COM-а если указатель на интерфейс передается в функцию как [in, out]-параметр, ArrRef-ов и релизов делать не надо. Можно конечно сделать эдреф и релиз внутри функции, но бес толку.

AddRef делается когда параметр идет как out и когда указатель помещается в некоторую переменную для долговременного хранеия. В C — это может быть только глобальная переменная.

VD>>Делать же Release переданного в функцию объекта вообще нельзя (по правилам COM).

H>Так я и не делаю — это освобождается после OleCreatePictureIndirect, а в функции свой Release есть еще.

Вот я и говорю.


BOOL SavePictureIndirect(PICTDESC pDesc,LPCTSTR szPath)
{
    IPicture* pPicture = NULL;
    BOOL bResult = TRUE;

    OleCreatePictureIndirect(&pDesc,&IID_IPicture,FALSE,(void**)&pPicture);
    if(pPicture == NULL)
        bResult = FALSE;
    else
    {
        // Вот это AddRef ...
        pPicture->lpVtbl->AddRef(pPicture);
        bResult = SavePicture(pPicture,szPath);
    }
    if(pPicture != NULL)
        pPicture->lpVtbl->Release(pPicture);
    return bResult;
}
// и вот этот релиз...
// ...
    while(FALSE);
    if(pPicture != NULL)
        pPicture->lpVtbl->Release(pPicture);
// ...


Лишние. Эти две ошибки друг друга компенсируют.

Если эти функции (по отдельности) начнут использовать другие программисты, с применением правил COM, будут вылезать ошибки подсчета ссылок.

VD>>И HRESULT проверять не мешало бы. :-\


H>Это не нужно — я проверяю на NULL.


Вот на NULL можно и не проверять, а HRESULT желательно. И без этого работать будет, но:

Если произойдет ошибка, но вызвавших твою функцию не сможет узнать в что произошло. Ну, и гипотетически возможен вариант, что произойдет не фатальная ошибка и ты ее проигнорируешь.
Короче, с COM-ом всегда лучше проверять hr-ы. Времени займет не много, а проблемы многие может снять.

VD>>А короче, можно... используем смартпоинтеры и библиотечные реализации стрима над файлом и будет короче.


H>Я взял чистый API — смартпоинтеры и библиотечные реализации стрима — по колву кода будет длиннее.


Я сразу не въехал, что код на C. Но кому нужен сегодня чистый C. Хотя для примера — это хорошо. Только я бы все равно его на C++ писал бы.

Кстати, в любом случае запись стрима в файл лучше вынести в отдельную функцию.

VD>>Ты бы малость причесал этот код и можно было бы его в журнал (RSDN Mag) сунуть как FAQ.


H>А поподробнее можно ?


1. Устранить проблемы с AddRef/Release.
2. Добавить комментарии и введение (в чем сложность?... основание для такого решения...).
3. (Опшонал) Переписать на C++. (кстати, очень приветствуются многоязычные варианты... VB, Delphi, .Net... но только у них у всех есть собственные встроенные средства)
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Сохранение битмапа - кто короче ?
От: ole! США http://files.rsdn.org/4543/rsdn.gif
Дата: 28.11.01 11:30
Оценка: 17 (3)
Здравствуйте Hollander, Вы писали:


H>Привет !


H>Встретил топик похожий, налабал решение попроще. Так можно сохранять хучь битмапы, хучь иконки, хучь метафайлы и вообще все объекты, поддерживающие IPicture. Написал на Cи — так круче.


даешь GDI+ в массы!!!



/// пример не в тему, зато позволяет сохранять в bmp,jpeg,gif,emf,tiff,png
/// содран с PlatformSDK/GDI+/Setting JPEG Compression Level

#include <Stdio.h>
#include <Objbase.h>
#include <Windows.h>
#include <Gdiplus.h>
using namespace Gdiplus;

// Helper functions
int GetCodecClsid(const WCHAR*, CLSID*);

int main()
{
   CLSID              codecClsid;
   EncoderParameters  encoderParameters;
   long               quality;
   Status             stat;

   // Get an image from the disk.
   Image image(L"Shapes.bmp");

   // Get the CLSID of the JPEG codec.
   GetCodecClsid(L"image/jpeg", &codecClsid);

   // Before we call Image::Save, we must initialize an
   // EncoderParameters object. The EncoderParameters object
   // has an array of EncoderParameter objects. In this
   // case, there is only one EncoderParameter object in the array.
   // The one EncoderParameter object has an array of values.
   // In this case, there is only one value (of type LONG)
   // in the array. We will set this value to 0, 50, and 100.

   encoderParameters.Count = 1;
   encoderParameters.Parameter[0].Guid = EncoderQuality;
   encoderParameters.Parameter[0].Type = ValueTypeLong;
   encoderParameters.Parameter[0].NumberOfValues = 1;

   // Save the image as a JPEG with quality level 0.
   quality = 0;
   encoderParameters.Parameter[0].Value = &quality;
   stat = image.Save(L"Shapes001.jpg", &codecClsid, &encoderParameters);

   if(stat == Ok)
      wprintf(L"%s saved successfully.\n", L"Shapes001.jpg");
   else
      wprintf(L"%d  Attempt to save %s failed.\n", stat, L"Shapes001.jpg");

   // Save the image as a JPEG with quality level 50.
   quality = 50;
   encoderParameters.Parameter[0].Value = &quality;
   stat = image.Save(L"Shapes050.jpg", &codecClsid, &encoderParameters);

   if(stat == Ok)
      wprintf(L"%s saved successfully.\n", L"Shapes050.jpg");
   else
      wprintf(L"%d  Attempt to save %s failed.\n", stat, L"Shapes050.jpg");

   // Save the image as a JPEG with quality level 100.
   quality = 100;
   encoderParameters.Parameter[0].Value = &quality;
   stat = image.Save(L"Shapes100.jpg", &codecClsid, &encoderParameters);

   if(stat == Ok)
      wprintf(L"%s saved successfully.\n", L"Shapes100.jpg");
   else
      wprintf(L"%d  Attempt to save %s failed.\n", stat, L"Shapes100.jpg");

   return 0;
} // main




/// snipped
my $.02
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.