Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе показано, как использовать IWICImageEncoder для сохранения содержимого в виде ID2D1Image в закодированный файл изображения, например JPEG. Если вы разрабатываете приложение для Магазина Windows, вы можете дать пользователю возможность выбрать целевой файл с помощью Windows::Storage::Pickers::FileSavePicker.
Что нужно знать
Технологии
Необходимые условия
- Вам нужен объект ID2D1DeviceContext и объект, содержащий содержимое Direct2D, реализующее ID2D1Image, например ID2D1Effect или ID2D1Bitmap1.
Инструкции
Шаг 1. Получение целевого файла и потока
Если вы хотите разрешить пользователю выбрать целевой файл, можно использовать FileSavePicker, открыть возвращенный файл и получить IStream для использования с WIC.
Создайте Windows::Storage::Pickers::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 для преобразования потока файлов. API среды выполнения Windows представляют потоки с 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)
);
}