Сохранение содержимого Direct2D в файл изображения
В этом разделе показано, как использовать IWICImageEncoder для сохранения содержимого в форме ID2D1Image в закодированном файле изображения, например JPEG. Если вы пишете приложение Магазина Windows, пользователь может выбрать целевой файл с помощью Windows::Storage::P ickers::FileSavePicker.
Это важно знать
Технологии
Предварительные требования
- Вам потребуется объект ID2D1DeviceContext и объект, содержащий содержимое Direct2D , реализующее ID2D1Image , например ID2D1Effect или ID2D1Bitmap1.
Инструкции
Шаг 1. Получение целевого файла и потока
Если вы хотите разрешить пользователю выбрать целевой файл, можно использовать FileSavePicker, открыть возвращенный файл и получить IStream для использования с WIC.
Создайте windows::Storage::P ickers::FileSavePicker и задайте его параметры для файлов изображений. Вызовите метод PickSaveFileAsync .
Pickers::FileSavePicker^ savePicker = ref new Pickers::FileSavePicker();
auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
pngExtensions->Append(".png");
savePicker->FileTypeChoices->Insert("PNG file", pngExtensions);
auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
jpgExtensions->Append(".jpg");
savePicker->FileTypeChoices->Insert("JPEG file", jpgExtensions);
savePicker->DefaultFileExtension = ".jpg";
savePicker->SuggestedFileName = "SaveScreen";
savePicker->SuggestedStartLocation = Pickers::PickerLocationId::PicturesLibrary;
task<StorageFile^> fileTask(savePicker->PickSaveFileAsync());
Объявите обработчик завершения для запуска после возврата асинхронной операции выбора файлов. Используйте метод GetResults , чтобы получить файл и получить объект потока файлов.
task<StorageFile^> fileTask(savePicker->PickSaveFileAsync());
fileTask.then([=](StorageFile^ file) {
if (file != nullptr)
{
// User selects a file.
GUID wicFormat = GUID_ContainerFormatPng;
if (file->FileType == ".jpg")
{
wicFormat = GUID_ContainerFormatJpeg;
}
Получите IRandomAccessStream , вызвав OpenAsync в файле и получив результат асинхронной операции.
task<Streams::IRandomAccessStream^> createStreamTask(file->OpenAsync(FileAccessMode::ReadWrite));
createStreamTask.then([=](Streams::IRandomAccessStream^ randomAccessStream) {
Наконец, используйте метод CreateStreamOverRandomAccessStream для преобразования файлового потока. среда выполнения Windows API представляют потоки с помощью IRandomAccessStream, а WIC использует IStream.
ComPtr<IStream> stream;
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
);
Примечание
Чтобы использовать функцию CreateStreamOverRandomAccessStream , необходимо включить shcore.h в проект.
Шаг 2. Получение кодировщика точечного изображения и кадра WIC
IWICBitmapEncoder и IWICBitmapFrameEncode предоставляют функциональные возможности для сохранения данных образов в формате закодированного файла.
Создание и инициализация объектов кодировщика.
ComPtr<IWICBitmapEncoder> wicBitmapEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateEncoder(
wicFormat,
nullptr, // No preferred codec vendor.
&wicBitmapEncoder
)
);
DX::ThrowIfFailed(
wicBitmapEncoder->Initialize(
stream,
WICBitmapEncoderNoCache
)
);
ComPtr<IWICBitmapFrameEncode> wicFrameEncode;
DX::ThrowIfFailed(
wicBitmapEncoder->CreateNewFrame(
&wicFrameEncode,
nullptr // No encoder options.
)
);
DX::ThrowIfFailed(
wicFrameEncode->Initialize(nullptr)
);
Шаг 3. Получение IWICImageEncoder
IWICImageEncoder — это новый интерфейс в Windows 8. Его можно создать из IWICImagingFactory2, который расширяет IWICImagingFactory и является новым для Windows 8.
ComPtr<IWICImagingFactory2> m_wicFactory;
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
Вызовите IWICImagingFactory2::CreateImageEncoder. Первый параметр является ID2D1Device и должен быть устройством, на котором было создано изображение, на котором вы хотите закодировать. Вы не можете смешивать изображения из разных доменов ресурсов в одном IWICImageEncoder.
ComPtr<IWICImageEncoder> imageEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateImageEncoder(
d2dDevice.Get(),
&imageEncoder
)
);
Шаг 4. Запись содержимого Direct2D с помощью IWICImageEncoder
IWICImageEncoder может записать изображение Direct2D в кадр изображения, эскиз фрейма или эскиз контейнера. Затем можно использовать IWICBitmapEncoder и IWICBitmapFrameEncode для кодирования данных образа в файл в обычном режиме.
Запишите изображение Direct2D в кадр. В этом фрагменте кода мы напишем ID2D1Bitmap , содержащий растровое содержимое Direct2D. Однако можно предоставить любой интерфейс, реализующий ID2D1Image.
DX::ThrowIfFailed(
imageEncoder->WriteFrame(
d2dBitmap.Get(),
wicFrameEncode.Get(),
nullptr // Use default WICImageParameter options.
)
);
Примечание
Параметр ID2D1Image должен быть создан в id2D1Device , переданном в IWICImagingFactory2::CreateImageEncoder.
Зафиксируйте wic и потоковые ресурсы, чтобы завершить операцию.
DX::ThrowIfFailed(
wicFrameEncode->Commit()
);
DX::ThrowIfFailed(
wicBitmapEncoder->Commit()
);
// Flush all memory buffers to the next-level storage object.
DX::ThrowIfFailed(
stream->Commit(STGC_DEFAULT)
);
Теперь у вас есть файл, содержащий образ Direct2D .
Полный пример
Ниже приведен полный код для этого примера.
ComPtr<IWICImagingFactory2> m_wicFactory;
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
void SaveAsImageFile::SaveBitmapToFile()
{
// Prepare a file picker for customers to input image file name.
Pickers::FileSavePicker^ savePicker = ref new Pickers::FileSavePicker();
auto pngExtensions = ref new Platform::Collections::Vector<Platform::String^>();
pngExtensions->Append(".png");
savePicker->FileTypeChoices->Insert("PNG file", pngExtensions);
auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
jpgExtensions->Append(".jpg");
savePicker->FileTypeChoices->Insert("JPEG file", jpgExtensions);
auto bmpExtensions = ref new Platform::Collections::Vector<Platform::String^>();
bmpExtensions->Append(".bmp");
savePicker->FileTypeChoices->Insert("BMP file", bmpExtensions);
savePicker->DefaultFileExtension = ".png";
savePicker->SuggestedFileName = "SaveScreen";
savePicker->SuggestedStartLocation = Pickers::PickerLocationId::PicturesLibrary;
task<StorageFile^> fileTask(savePicker->PickSaveFileAsync());
fileTask.then([=](StorageFile^ file) {
if (file != nullptr)
{
// User selects a file.
m_imageFileName = file->Name;
GUID wicFormat = GUID_ContainerFormatPng;
if (file->FileType == ".bmp")
{
wicFormat = GUID_ContainerFormatBmp;
}
else if (file->FileType == ".jpg")
{
wicFormat = GUID_ContainerFormatJpeg;
}
// Retrieve a stream from the file.
task<Streams::IRandomAccessStream^> createStreamTask(file->OpenAsync(FileAccessMode::ReadWrite));
createStreamTask.then([=](Streams::IRandomAccessStream^ randomAccessStream) {
// Convert the RandomAccessStream to an IStream.
ComPtr<IStream> stream;
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
);
SaveBitmapToStream(m_d2dTargetBitmap, m_wicFactory, m_d2dContext, wicFormat, stream.Get());
});
}
});
}
// Save render target bitmap to a stream using WIC.
void SaveAsImageFile::SaveBitmapToStream(
_In_ ComPtr<ID2D1Bitmap1> d2dBitmap,
_In_ ComPtr<IWICImagingFactory2> wicFactory2,
_In_ ComPtr<ID2D1DeviceContext> d2dContext,
_In_ REFGUID wicFormat,
_In_ IStream* stream
)
{
// Create and initialize WIC Bitmap Encoder.
ComPtr<IWICBitmapEncoder> wicBitmapEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateEncoder(
wicFormat,
nullptr, // No preferred codec vendor.
&wicBitmapEncoder
)
);
DX::ThrowIfFailed(
wicBitmapEncoder->Initialize(
stream,
WICBitmapEncoderNoCache
)
);
// Create and initialize WIC Frame Encoder.
ComPtr<IWICBitmapFrameEncode> wicFrameEncode;
DX::ThrowIfFailed(
wicBitmapEncoder->CreateNewFrame(
&wicFrameEncode,
nullptr // No encoder options.
)
);
DX::ThrowIfFailed(
wicFrameEncode->Initialize(nullptr)
);
// Retrieve D2D Device.
ComPtr<ID2D1Device> d2dDevice;
d2dContext->GetDevice(&d2dDevice);
// Create IWICImageEncoder.
ComPtr<IWICImageEncoder> imageEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateImageEncoder(
d2dDevice.Get(),
&imageEncoder
)
);
DX::ThrowIfFailed(
imageEncoder->WriteFrame(
d2dBitmap.Get(),
wicFrameEncode.Get(),
nullptr // Use default WICImageParameter options.
)
);
DX::ThrowIfFailed(
wicFrameEncode->Commit()
);
DX::ThrowIfFailed(
wicBitmapEncoder->Commit()
);
// Flush all memory buffers to the next-level storage object.
DX::ThrowIfFailed(
stream->Commit(STGC_DEFAULT)
);
}