Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(831)

Side by Side Diff: win8/metro_driver/print_document_source.cc

Issue 10875008: Integrate the Windows 8 code into the Chromium tree. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove conflicting OWNERS file. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « win8/metro_driver/print_document_source.h ('k') | win8/metro_driver/print_handler.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "stdafx.h"
6 #include "win8/metro_driver/print_document_source.h"
7
8 #include <windows.graphics.display.h>
9
10 #include "base/logging.h"
11
12
13 namespace {
14
15 class D2DFactoryAutoLock {
16 public:
17 explicit D2DFactoryAutoLock(ID2D1Factory* d2d_factory) {
18 HRESULT hr = d2d_factory->QueryInterface(IID_PPV_ARGS(&d2d_multithread_));
19 if (d2d_multithread_.Get())
20 d2d_multithread_->Enter();
21 else
22 NOTREACHED() << "Failed to QI for ID2D1Multithread " << std::hex << hr;
23 }
24
25 ~D2DFactoryAutoLock() {
26 if (d2d_multithread_.Get())
27 d2d_multithread_->Leave();
28 }
29
30 private:
31 mswr::ComPtr<ID2D1Multithread> d2d_multithread_;
32 };
33
34 // TODO(mad): remove once we don't run mixed SDK/OS anymore.
35 const GUID kOldPackageTargetGuid =
36 {0xfb2a33c0, 0x8c35, 0x465f,
37 {0xbe, 0xd5, 0x9f, 0x36, 0x89, 0x51, 0x77, 0x52}};
38 const GUID kNewPackageTargetGuid =
39 {0x1a6dd0ad, 0x1e2a, 0x4e99,
40 {0xa5, 0xba, 0x91, 0xf1, 0x78, 0x18, 0x29, 0x0e}};
41
42
43 } // namespace
44
45 namespace metro_driver {
46
47 PrintDocumentSource::PrintDocumentSource()
48 : page_count_ready_(true, false),
49 parent_lock_(NULL),
50 height_(0),
51 width_(0),
52 dpi_(96),
53 aborted_(false),
54 using_old_preview_interface_(false) {
55 }
56
57 HRESULT PrintDocumentSource::RuntimeClassInitialize(
58 const DirectXContext& directx_context,
59 base::Lock* parent_lock) {
60 DCHECK(parent_lock != NULL);
61 DCHECK(directx_context.d2d_context.Get() != NULL);
62 DCHECK(directx_context.d2d_device.Get() != NULL);
63 DCHECK(directx_context.d2d_factory.Get() != NULL);
64 DCHECK(directx_context.d3d_device.Get() != NULL);
65 DCHECK(directx_context.wic_factory.Get() != NULL);
66 directx_context_ = directx_context;
67
68 // No other method can be called before RuntimeClassInitialize which is called
69 // during the construction via mswr::MakeAndInitialize(), so it's safe for all
70 // other methods to use the parent_lock_ without checking if it's NULL.
71 DCHECK(parent_lock_ == NULL);
72 parent_lock_ = parent_lock;
73
74 return S_OK;
75 }
76
77 void PrintDocumentSource::Abort() {
78 base::AutoLock lock(*parent_lock_);
79 aborted_ = true;
80 if (page_count_ready_.IsSignaled()) {
81 pages_.clear();
82 for (size_t i = 0; i < pages_ready_state_.size(); ++i)
83 pages_ready_state_[i]->Broadcast();
84 } else {
85 DCHECK(pages_.empty() && pages_ready_state_.empty());
86 }
87 }
88
89 STDMETHODIMP PrintDocumentSource::GetPreviewPageCollection(
90 IPrintDocumentPackageTarget* package_target,
91 IPrintPreviewPageCollection** page_collection) {
92 DVLOG(1) << __FUNCTION__;
93 DCHECK(package_target != NULL);
94 DCHECK(page_collection != NULL);
95
96 HRESULT hr = package_target->GetPackageTarget(
97 __uuidof(IPrintPreviewDxgiPackageTarget),
98 IID_PPV_ARGS(&dxgi_preview_target_));
99 if (FAILED(hr)) {
100 // TODO(mad): remove once we don't run mixed SDK/OS anymore.
101 // The IID changed from one version of the SDK to another, so try the other
102 // one in case we are running a build from a different SDK than the one
103 // related to the OS version we are running.
104 GUID package_target_uuid = kNewPackageTargetGuid;
105 if (package_target_uuid == __uuidof(IPrintPreviewDxgiPackageTarget)) {
106 package_target_uuid = kOldPackageTargetGuid;
107 using_old_preview_interface_ = true;
108 }
109 hr = package_target->GetPackageTarget(package_target_uuid,
110 package_target_uuid,
111 &dxgi_preview_target_);
112 if (FAILED(hr)) {
113 LOG(ERROR) << "Failed to get IPrintPreviewDXGIPackageTarget " << std::hex
114 << hr;
115 return hr;
116 }
117 } else {
118 using_old_preview_interface_ = (__uuidof(IPrintPreviewDxgiPackageTarget) ==
119 kOldPackageTargetGuid);
120 }
121
122 mswr::ComPtr<IPrintPreviewPageCollection> preview_page_collection;
123 mswr::ComPtr<PrintDocumentSource> print_document_source(this);
124 hr = print_document_source.As(&preview_page_collection);
125 if (FAILED(hr)) {
126 LOG(ERROR) << "Failed to get preview_page_collection " << std::hex << hr;
127 return hr;
128 }
129
130 hr = preview_page_collection.CopyTo(page_collection);
131 if (FAILED(hr)) {
132 LOG(ERROR) << "Failed to copy preview_page_collection " << std::hex << hr;
133 return hr;
134 }
135 return hr;
136 }
137
138 STDMETHODIMP PrintDocumentSource::MakeDocument(
139 IInspectable* options,
140 IPrintDocumentPackageTarget* package_target) {
141 DVLOG(1) << __FUNCTION__;
142 DCHECK(options != NULL);
143 DCHECK(package_target != NULL);
144
145 mswr::ComPtr<wingfx::Printing::IPrintTaskOptionsCore> print_task_options;
146 HRESULT hr = options->QueryInterface(
147 wingfx::Printing::IID_IPrintTaskOptionsCore,
148 reinterpret_cast<void**>(print_task_options.GetAddressOf()));
149 if (FAILED(hr)) {
150 LOG(ERROR) << "Failed to QI for IPrintTaskOptionsCore " << std::hex << hr;
151 return hr;
152 }
153
154 // Use the first page's description for the whole document. Page numbers
155 // are 1-based in this context.
156 // TODO(mad): Check if it would be useful to use per page descriptions.
157 wingfx::Printing::PrintPageDescription page_desc = {};
158 hr = print_task_options->GetPageDescription(1 /* page */, &page_desc);
159 if (FAILED(hr)) {
160 LOG(ERROR) << "Failed to GetPageDescription " << std::hex << hr;
161 return hr;
162 }
163
164 D2D1_PRINT_CONTROL_PROPERTIES print_control_properties;
165 if (page_desc.DpiX > page_desc.DpiY)
166 print_control_properties.rasterDPI = static_cast<float>(page_desc.DpiY);
167 else
168 print_control_properties.rasterDPI = static_cast<float>(page_desc.DpiX);
169
170 // Color space for vector graphics in D2D print control.
171 print_control_properties.colorSpace = D2D1_COLOR_SPACE_SRGB;
172 print_control_properties.fontSubset = D2D1_PRINT_FONT_SUBSET_MODE_DEFAULT;
173
174 mswr::ComPtr<ID2D1PrintControl> print_control;
175 hr = directx_context_.d2d_device->CreatePrintControl(
176 directx_context_.wic_factory.Get(),
177 package_target,
178 print_control_properties,
179 print_control.GetAddressOf());
180 if (FAILED(hr)) {
181 LOG(ERROR) << "Failed to CreatePrintControl " << std::hex << hr;
182 return hr;
183 }
184
185 D2D1_SIZE_F page_size = D2D1::SizeF(page_desc.PageSize.Width,
186 page_desc.PageSize.Height);
187
188 // Wait for the number of pages to be available.
189 // If an abort occured, we'll get 0 and won't enter the loop below.
190 size_t page_count = WaitAndGetPageCount();
191
192 mswr::ComPtr<ID2D1GdiMetafile> gdi_metafile;
193 for (size_t page = 0; page < page_count; ++page) {
194 gdi_metafile.Reset();
195 hr = WaitAndGetPage(page, gdi_metafile.GetAddressOf());
196 LOG_IF(ERROR, FAILED(hr)) << "Failed to get page's metafile " << std::hex
197 << hr;
198 // S_FALSE means we got aborted.
199 if (hr == S_FALSE || FAILED(hr))
200 break;
201 hr = PrintPage(print_control.Get(), gdi_metafile.Get(), page_size);
202 if (FAILED(hr))
203 break;
204 }
205
206 HRESULT close_hr = print_control->Close();
207 if (FAILED(close_hr) && SUCCEEDED(hr))
208 return close_hr;
209 else
210 return hr;
211 }
212
213 STDMETHODIMP PrintDocumentSource::Paginate(uint32 page,
214 IInspectable* options) {
215 DVLOG(1) << __FUNCTION__ << ", page = " << page;
216 DCHECK(options != NULL);
217 // GetPreviewPageCollection must have been successfuly called.
218 DCHECK(dxgi_preview_target_.Get() != NULL);
219
220 // Get print settings from PrintTaskOptions for preview, such as page
221 // description, which contains page size, imageable area, DPI.
222 // TODO(mad): obtain other print settings in the same way, such as ColorMode,
223 // NumberOfCopies, etc...
224 mswr::ComPtr<wingfx::Printing::IPrintTaskOptionsCore> print_options;
225 HRESULT hr = options->QueryInterface(
226 wingfx::Printing::IID_IPrintTaskOptionsCore,
227 reinterpret_cast<void**>(print_options.GetAddressOf()));
228 if (FAILED(hr)) {
229 LOG(ERROR) << "Failed to QI for IPrintTaskOptionsCore " << std::hex << hr;
230 return hr;
231 }
232
233 wingfx::Printing::PrintPageDescription page_desc = {};
234 hr = print_options->GetPageDescription(1 /* page */, &page_desc);
235 if (FAILED(hr)) {
236 LOG(ERROR) << "Failed to GetPageDescription " << std::hex << hr;
237 return hr;
238 }
239
240 width_ = page_desc.PageSize.Width;
241 height_ = page_desc.PageSize.Height;
242
243 hr = dxgi_preview_target_->InvalidatePreview();
244 if (FAILED(hr)) {
245 LOG(ERROR) << "Failed to InvalidatePreview " << std::hex << hr;
246 return hr;
247 }
248
249 size_t page_count = WaitAndGetPageCount();
250 // A page_count of 0 means abort...
251 if (page_count == 0)
252 return S_FALSE;
253 hr = dxgi_preview_target_->SetJobPageCount(PageCountType::FinalPageCount,
254 page_count);
255 if (FAILED(hr)) {
256 LOG(ERROR) << "Failed to SetJobPageCount " << std::hex << hr;
257 return hr;
258 }
259 return hr;
260 }
261
262 STDMETHODIMP PrintDocumentSource::MakePage(uint32 job_page,
263 float width,
264 float height) {
265 DVLOG(1) << __FUNCTION__ << ", width: " << width << ", height: " << height
266 << ", job_page: " << job_page;
267 DCHECK(width > 0 && height > 0);
268 // Paginate must have been called before this.
269 if (width_ <= 0.0 || height_ <= 0.0)
270 return S_FALSE;
271
272 // When job_page is JOB_PAGE_APPLICATION_DEFINED, it means a new preview
273 // begins. TODO(mad): Double check if we need to cancel pending resources.
274 if (job_page == JOB_PAGE_APPLICATION_DEFINED)
275 job_page = 1;
276
277 winfoundtn::Size preview_size;
278 preview_size.Width = width;
279 preview_size.Height = height;
280 float scale = width_ / width;
281
282 mswr::ComPtr<ID2D1Factory> factory;
283 directx_context_.d2d_device->GetFactory(&factory);
284
285 mswr::ComPtr<ID2D1GdiMetafile> gdi_metafile;
286 HRESULT hr = WaitAndGetPage(job_page - 1, gdi_metafile.GetAddressOf());
287 LOG_IF(ERROR, FAILED(hr)) << "Failed to get page's metafile " << std::hex
288 << hr;
289 // Again, S_FALSE means we got aborted.
290 if (hr == S_FALSE || FAILED(hr))
291 return hr;
292
293 // We are accessing D3D resources directly without D2D's knowledge, so we
294 // must manually acquire the D2D factory lock.
295 D2DFactoryAutoLock factory_lock(directx_context_.d2d_factory.Get());
296
297 CD3D11_TEXTURE2D_DESC texture_desc(
298 DXGI_FORMAT_B8G8R8A8_UNORM,
299 static_cast<UINT32>(ceil(width * dpi_ / 96)),
300 static_cast<UINT32>(ceil(height * dpi_ / 96)),
301 1,
302 1,
303 D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE
304 );
305 mswr::ComPtr<ID3D11Texture2D> texture;
306 hr = directx_context_.d3d_device->CreateTexture2D(
307 &texture_desc, NULL, &texture);
308 if (FAILED(hr)) {
309 LOG(ERROR) << "Failed to create a 2D texture " << std::hex << hr;
310 return hr;
311 }
312
313 mswr::ComPtr<IDXGISurface> dxgi_surface;
314 hr = texture.As<IDXGISurface>(&dxgi_surface);
315 if (FAILED(hr)) {
316 LOG(ERROR) << "Failed to QI for IDXGISurface " << std::hex << hr;
317 return hr;
318 }
319
320 // D2D device contexts are stateful, and hence a unique device context must
321 // be used on each call.
322 mswr::ComPtr<ID2D1DeviceContext> d2d_context;
323 hr = directx_context_.d2d_device->CreateDeviceContext(
324 D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d_context);
325
326 d2d_context->SetDpi(dpi_, dpi_);
327
328 mswr::ComPtr<ID2D1Bitmap1> d2dSurfaceBitmap;
329 hr = d2d_context->CreateBitmapFromDxgiSurface(dxgi_surface.Get(),
330 NULL, // default properties.
331 &d2dSurfaceBitmap);
332 if (FAILED(hr)) {
333 LOG(ERROR) << "Failed to CreateBitmapFromDxgiSurface " << std::hex << hr;
334 return hr;
335 }
336
337 d2d_context->SetTarget(d2dSurfaceBitmap.Get());
338 d2d_context->BeginDraw();
339 d2d_context->Clear();
340 d2d_context->SetTransform(D2D1::Matrix3x2F(1/scale, 0, 0, 1/scale, 0, 0));
341 d2d_context->DrawGdiMetafile(gdi_metafile.Get());
342
343 hr = d2d_context->EndDraw();
344 if (FAILED(hr)) {
345 LOG(ERROR) << "Failed to EndDraw " << std::hex << hr;
346 return hr;
347 }
348
349 // TODO(mad): remove once we don't run mixed SDK/OS anymore.
350 #ifdef __IPrintPreviewDxgiPackageTarget_FWD_DEFINED__
351 FLOAT dpi = dpi_;
352 if (using_old_preview_interface_) {
353 // We compiled with the new API but run on the old OS, so we must cheat
354 // and send something that looks like a float but has a UINT32 value.
355 *reinterpret_cast<UINT32*>(&dpi) = static_cast<UINT32>(dpi_);
356 }
357 #else
358 UINT32 dpi = static_cast<UINT32>(dpi_);
359 if (!using_old_preview_interface_) {
360 // We compiled with the old API but run on the new OS, so we must cheat
361 // and send something that looks like a UINT32 but has a float value.
362 *reinterpret_cast<FLOAT*>(&dpi) = dpi_;
363 }
364 #endif // __IPrintPreviewDxgiPackageTarget_FWD_DEFINED__
365 hr = dxgi_preview_target_->DrawPage(job_page, dxgi_surface.Get(), dpi, dpi);
366 if (FAILED(hr)) {
367 LOG(ERROR) << "Failed to DrawPage " << std::hex << hr;
368 return hr;
369 }
370 return hr;
371 }
372
373 void PrintDocumentSource::ResetDpi(float dpi) {
374 {
375 base::AutoLock lock(*parent_lock_);
376 if (dpi == dpi_)
377 return;
378 dpi_ = dpi;
379 }
380 directx_context_.d2d_context->SetDpi(dpi, dpi);
381 }
382
383 void PrintDocumentSource::SetPageCount(size_t page_count) {
384 DCHECK(page_count > 0);
385 {
386 base::AutoLock lock(*parent_lock_);
387 DCHECK(!page_count_ready_.IsSignaled());
388 DCHECK(pages_.empty() && pages_ready_state_.empty());
389
390 pages_.resize(page_count);
391 pages_ready_state_.resize(page_count);
392
393 for (size_t i = 0; i < page_count; ++i)
394 pages_ready_state_[i].reset(new base::ConditionVariable(parent_lock_));
395 }
396 page_count_ready_.Signal();
397 }
398
399 void PrintDocumentSource::AddPage(size_t page_number,
400 IStream* metafile_stream) {
401 DCHECK(metafile_stream != NULL);
402 base::AutoLock lock(*parent_lock_);
403
404 DCHECK(page_count_ready_.IsSignaled());
405 DCHECK(page_number < pages_.size());
406
407 pages_[page_number] = metafile_stream;
408 pages_ready_state_[page_number]->Signal();
409 }
410
411 HRESULT PrintDocumentSource::PrintPage(ID2D1PrintControl* print_control,
412 ID2D1GdiMetafile* gdi_metafile,
413 D2D1_SIZE_F page_size) {
414 DVLOG(1) << __FUNCTION__ << ", page_size: (" << page_size.width << ", "
415 << page_size.height << ")";
416 DCHECK(print_control != NULL);
417 DCHECK(gdi_metafile != NULL);
418
419 // D2D device contexts are stateful, and hence a unique device context must
420 // be used on each call.
421 mswr::ComPtr<ID2D1DeviceContext> d2d_context;
422 HRESULT hr = directx_context_.d2d_device->CreateDeviceContext(
423 D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d_context);
424 if (FAILED(hr)) {
425 LOG(ERROR) << "Failed to CreateDeviceContext " << std::hex << hr;
426 return hr;
427 }
428
429 mswr::ComPtr<ID2D1CommandList> print_command_list;
430 hr = d2d_context->CreateCommandList(&print_command_list);
431 if (FAILED(hr)) {
432 LOG(ERROR) << "Failed to CreateCommandList " << std::hex << hr;
433 return hr;
434 }
435
436 d2d_context->SetTarget(print_command_list.Get());
437
438 d2d_context->BeginDraw();
439 d2d_context->DrawGdiMetafile(gdi_metafile);
440 hr = d2d_context->EndDraw();
441 LOG_IF(ERROR, FAILED(hr)) << "Failed to EndDraw " << std::hex << hr;
442
443 // Make sure to always close the command list.
444 HRESULT close_hr = print_command_list->Close();
445 LOG_IF(ERROR, FAILED(close_hr)) << "Failed to close command list " << std::hex
446 << hr;
447 if (SUCCEEDED(hr) && SUCCEEDED(close_hr))
448 hr = print_control->AddPage(print_command_list.Get(), page_size, NULL);
449 if (FAILED(hr))
450 return hr;
451 else
452 return close_hr;
453 }
454
455 size_t PrintDocumentSource::WaitAndGetPageCount() {
456 // Properly protect the wait/access to the page count.
457 {
458 base::AutoLock lock(*parent_lock_);
459 if (aborted_)
460 return 0;
461 DCHECK(pages_.size() == pages_ready_state_.size());
462 if (!pages_.empty())
463 return pages_.size();
464 }
465 page_count_ready_.Wait();
466 {
467 base::AutoLock lock(*parent_lock_);
468 if (!aborted_) {
469 DCHECK(pages_.size() == pages_ready_state_.size());
470 return pages_.size();
471 }
472 }
473 // A page count of 0 means abort.
474 return 0;
475 }
476
477 HRESULT PrintDocumentSource::WaitAndGetPage(size_t page_number,
478 ID2D1GdiMetafile** gdi_metafile) {
479 // Properly protect the wait/access to the page data.
480 base::AutoLock lock(*parent_lock_);
481 // Make sure we weren't canceled before getting here.
482 // And the page count should have been received before we get here too.
483 if (aborted_)
484 return S_FALSE;
485
486 // We shouldn't be asked for a page until we got the page count.
487 DCHECK(page_count_ready_.IsSignaled());
488 DCHECK(page_number <= pages_ready_state_.size());
489 DCHECK(pages_.size() == pages_ready_state_.size());
490 while (!aborted_ && pages_[page_number].Get() == NULL)
491 pages_ready_state_[page_number]->Wait();
492
493 // Make sure we weren't aborted while we waited unlocked.
494 if (aborted_)
495 return S_FALSE;
496 DCHECK(page_number < pages_.size());
497
498 mswr::ComPtr<ID2D1Factory> factory;
499 directx_context_.d2d_device->GetFactory(&factory);
500
501 mswr::ComPtr<ID2D1Factory1> factory1;
502 HRESULT hr = factory.As(&factory1);
503 if (FAILED(hr)) {
504 LOG(ERROR) << "Failed to QI for ID2D1Factory1 " << std::hex << hr;
505 return hr;
506 }
507
508 ULARGE_INTEGER result;
509 LARGE_INTEGER seek_pos;
510 seek_pos.QuadPart = 0;
511 hr = pages_[page_number]->Seek(seek_pos, STREAM_SEEK_SET, &result);
512 if (FAILED(hr)) {
513 LOG(ERROR) << "Failed to Seek page stream " << std::hex << hr;
514 return hr;
515 }
516
517 hr = factory1->CreateGdiMetafile(pages_[page_number].Get(), gdi_metafile);
518 if (FAILED(hr)) {
519 LOG(ERROR) << "Failed to CreateGdiMetafile " << std::hex << hr;
520 return hr;
521 }
522 return hr;
523 }
524
525 } // namespace metro_driver
OLDNEW
« no previous file with comments | « win8/metro_driver/print_document_source.h ('k') | win8/metro_driver/print_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698