xref: /haiku/src/libs/print/libprint/GraphicsDriver.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
1 /*
2  * GraphicsDriver.cpp
3  * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4  */
5 
6 #include <algorithm>
7 #include <cstdio>
8 #include <cstdarg>
9 
10 #include <Alert.h>
11 #include <Bitmap.h>
12 #include <Debug.h>
13 #include <Message.h>
14 #include <PrintJob.h>
15 #include <Region.h>
16 #include <TextControl.h>
17 #include <TextControl.h>
18 #include <StopWatch.h>
19 #include <View.h>
20 #include <Directory.h>
21 #include <File.h>
22 
23 #include "GraphicsDriver.h"
24 #include "PrintProcess.h"
25 #include "JobData.h"
26 #include "PrinterData.h"
27 #include "PrinterCap.h"
28 #include "Preview.h"
29 #include "Transport.h"
30 #include "ValidRect.h"
31 #include "DbgMsg.h"
32 
33 
34 using namespace std;
35 
36 
37 // Measure printJob() time. Either true or false.
38 #define MEASURE_PRINT_JOB_TIME false
39 
40 
41 enum {
42 	kMaxMemorySize = 4 * 1024 * 1024
43 };
44 
45 
46 GraphicsDriver::GraphicsDriver(BMessage* message, PrinterData* printerData,
47 	const PrinterCap* printerCap)
48 	:
49 	fMessage(message),
50 	fView(NULL),
51 	fBitmap(NULL),
52 	fRotatedBitmap(NULL),
53 	fTransport(NULL),
54 	fOrgJobData(NULL),
55 	fRealJobData(NULL),
56 	fPrinterData(printerData),
57 	fPrinterCap(printerCap),
58 	fSpoolMetaData(NULL),
59 	fPageWidth(0),
60 	fPageHeight(0),
61 	fBandWidth(0),
62 	fBandHeight(0),
63 	fPixelDepth(0),
64 	fBandCount(0),
65 	fInternalCopies(0),
66 	fPageCount(0),
67 	fStatusWindow(NULL)
68 {
69 }
70 
71 
72 GraphicsDriver::~GraphicsDriver()
73 {
74 }
75 
76 
77 static BRect
78 RotateRect(BRect rect)
79 {
80 	BRect rotated(rect.top, rect.left, rect.bottom, rect.right);
81 	return rotated;
82 }
83 
84 
85 bool
86 GraphicsDriver::_SetupData(BFile* spoolFile)
87 {
88 	if (fOrgJobData != NULL) {
89 		// already initialized
90 		return true;
91 	}
92 
93 #ifndef B_BEOS_VERSION_DANO
94 	print_file_header pfh;
95 #else
96 	BPrintJob::print_file_header pfh;
97 #endif
98 	spoolFile->Seek(0, SEEK_SET);
99 	spoolFile->Read(&pfh, sizeof(pfh));
100 
101 	DBGMSG(("print_file_header::version = 0x%x\n",  pfh.version));
102 	DBGMSG(("print_file_header::page_count = %d\n", pfh.page_count));
103 	DBGMSG(("print_file_header::first_page = 0x%x\n", (int)pfh.first_page));
104 
105 	if (pfh.page_count <= 0) {
106 		// nothing to print
107 		return false;
108 	}
109 
110 	fPageCount = (uint32) pfh.page_count;
111 	BMessage *msg = new BMessage();
112 	msg->Unflatten(spoolFile);
113 	fOrgJobData = new JobData(msg, fPrinterCap, JobData::kJobSettings);
114 	DUMP_BMESSAGE(msg);
115 	delete msg;
116 
117 	fRealJobData = new JobData(*fOrgJobData);
118 
119 	switch (fOrgJobData->GetNup()) {
120 	case 2:
121 	case 8:
122 	case 32:
123 	case 128:
124 		fRealJobData->SetPrintableRect(
125 			RotateRect(fOrgJobData->GetPrintableRect()));
126 		fRealJobData->SetScaledPrintableRect(
127 			RotateRect(fOrgJobData->GetScaledPrintableRect()));
128 		fRealJobData->SetPhysicalRect(
129 			RotateRect(fOrgJobData->GetPhysicalRect()));
130 		fRealJobData->SetScaledPhysicalRect(
131 			RotateRect(fOrgJobData->GetScaledPhysicalRect()));
132 
133 		if (JobData::kPortrait == fOrgJobData->GetOrientation())
134 			fRealJobData->SetOrientation(JobData::kLandscape);
135 		else
136 			fRealJobData->SetOrientation(JobData::kPortrait);
137 		break;
138 	}
139 
140 	if (fOrgJobData->GetCollate() && fPageCount > 1) {
141 		fRealJobData->SetCopies(1);
142 		fInternalCopies = fOrgJobData->GetCopies();
143 	} else {
144 		fInternalCopies = 1;
145 	}
146 
147 	fSpoolMetaData = new SpoolMetaData(spoolFile);
148 	return true;
149 }
150 
151 
152 void
153 GraphicsDriver::_CleanupData()
154 {
155 	delete fRealJobData;
156 	delete fOrgJobData;
157 	delete fSpoolMetaData;
158 	fRealJobData   = NULL;
159 	fOrgJobData    = NULL;
160 	fSpoolMetaData = NULL;
161 }
162 
163 
164 void
165 GraphicsDriver::_SetupBitmap()
166 {
167 	fPixelDepth = color_space2pixel_depth(fOrgJobData->GetSurfaceType());
168 
169 	fPageWidth  = (fRealJobData->GetPhysicalRect().IntegerWidth()
170 		* fOrgJobData->GetXres() + 71) / 72;
171 	fPageHeight = (fRealJobData->GetPhysicalRect().IntegerHeight()
172 		* fOrgJobData->GetYres() + 71) / 72;
173 
174 	int widthByte = (fPageWidth * fPixelDepth + 7) / 8;
175 	int size = widthByte * fPageHeight;
176 #ifdef USE_PREVIEW_FOR_DEBUG
177 	size = 0;
178 #endif
179 
180 	if (size < kMaxMemorySize) {
181 		fBandCount  = 0;
182 		fBandWidth  = fPageWidth;
183 		fBandHeight = fPageHeight;
184 	} else {
185 		fBandCount  = (size + kMaxMemorySize - 1) / kMaxMemorySize;
186 		if (_NeedRotateBitmapBand()) {
187 			fBandWidth  = (fPageWidth + fBandCount - 1) / fBandCount;
188 			fBandHeight = fPageHeight;
189 		} else {
190 			fBandWidth  = fPageWidth;
191 			fBandHeight = (fPageHeight + fBandCount - 1) / fBandCount;
192 		}
193 	}
194 
195 	DBGMSG(("****************\n"));
196 	DBGMSG(("page_width  = %d\n", fPageWidth));
197 	DBGMSG(("page_height = %d\n", fPageHeight));
198 	DBGMSG(("band_count  = %d\n", fBandCount));
199 	DBGMSG(("band_height = %d\n", fBandHeight));
200 	DBGMSG(("****************\n"));
201 
202 	BRect rect;
203 	rect.Set(0, 0, fBandWidth - 1, fBandHeight - 1);
204 	fBitmap = new BBitmap(rect, fOrgJobData->GetSurfaceType(), true);
205 	fView   = new BView(rect, "", B_FOLLOW_ALL, B_WILL_DRAW);
206 	fBitmap->AddChild(fView);
207 
208 	if (_NeedRotateBitmapBand()) {
209 		BRect rotatedRect(0, 0, rect.bottom, rect.right);
210 		fRotatedBitmap = new BBitmap(rotatedRect, fOrgJobData->GetSurfaceType(),
211 			false);
212 	}
213 }
214 
215 
216 void
217 GraphicsDriver::_CleanupBitmap()
218 {
219 	delete fBitmap;
220 	fBitmap = NULL;
221 	fView   = NULL;
222 
223 	delete fRotatedBitmap;
224 	fRotatedBitmap = NULL;
225 }
226 
227 
228 BPoint
229 GraphicsDriver::GetScale(int32 nup, BRect physicalRect, float scaling)
230 {
231 	float width;
232 	float height;
233 	BPoint scale;
234 
235 	scale.x = scale.y = 1.0f;
236 
237 	switch (nup) {
238 	case 1:
239 		scale.x = scale.y = 1.0f;
240 		break;
241 	case 2:	/* 1x2 or 2x1 */
242 		width  = physicalRect.Width();
243 		height = physicalRect.Height();
244 		if (width < height) {	// portrait
245 			scale.x = height / 2.0f / width;
246 			scale.y = width / height;
247 		} else {	// landscape
248 			scale.x = height / width;
249 			scale.y = width / 2.0f / height;
250 		}
251 		break;
252 	case 8:	/* 2x4 or 4x2 */
253 		width  = physicalRect.Width();
254 		height = physicalRect.Height();
255 		if (width < height) {
256 			scale.x = height / 4.0f / width;
257 			scale.y = width / height / 2.0f;
258 		} else {
259 			scale.x = height / width / 2.0f;
260 			scale.y = width / 4.0f / height;
261 		}
262 		break;
263 	case 32:	/* 4x8 or 8x4 */
264 		width  = physicalRect.Width();
265 		height = physicalRect.Height();
266 		if (width < height) {
267 			scale.x = height / 8.0f / width;
268 			scale.y = width / height / 4.0f;
269 		} else {
270 			scale.x = height / width / 4.0f;
271 			scale.y = width / 8.0f / height;
272 		}
273 		break;
274 	case 4:		/* 2x2 */
275 		scale.x = scale.y = 1.0f / 2.0f;
276 		break;
277 	case 9:		/* 3x3 */
278 		scale.x = scale.y = 1.0f / 3.0f;
279 		break;
280 	case 16:	/* 4x4 */
281 		scale.x = scale.y = 1.0f / 4.0f;
282 		break;
283 	case 25:	/* 5x5 */
284 		scale.x = scale.y = 1.0f / 5.0f;
285 		break;
286 	case 36:	/* 6x6 */
287 		scale.x = scale.y = 1.0f / 6.0f;
288 		break;
289 	case 49:	/* 7x7 */
290 		scale.x = scale.y = 1.0f / 7.0f;
291 		break;
292 	case 64:	/* 8x8 */
293 		scale.x = scale.y = 1.0f / 8.0f;
294 		break;
295 	case 81:	/* 9x9 */
296 		scale.x = scale.y = 1.0f / 9.0f;
297 		break;
298 	case 100:	/* 10x10 */
299 		scale.x = scale.y = 1.0f / 10.0f;
300 		break;
301 	case 121:	/* 11x11 */
302 		scale.x = scale.y = 1.0f / 11.0f;
303 		break;
304 	}
305 
306 	scale.x = scale.x * scaling / 100.0;
307 	scale.y = scale.y * scaling / 100.0;
308 
309 	return scale;
310 }
311 
312 
313 BPoint
314 GraphicsDriver::GetOffset(int32 nup, int index,
315 	JobData::Orientation orientation, const BPoint* scale,
316 	BRect scaledPhysicalRect, BRect scaledPrintableRect,
317 	BRect physicalRect)
318 {
319 	BPoint offset;
320 	offset.x = 0;
321 	offset.y = 0;
322 
323 	float width  = scaledPhysicalRect.Width();
324 	float height = scaledPhysicalRect.Height();
325 
326 	switch (nup) {
327 	case 1:
328 		break;
329 	case 2:
330 		if (index == 1) {
331 			if (JobData::kPortrait == orientation) {
332 				offset.x = width;
333 			} else {
334 				offset.y = height;
335 			}
336 		}
337 		break;
338 	case 8:
339 		if (JobData::kPortrait == orientation) {
340 			offset.x = width  * (index / 2);
341 			offset.y = height * (index % 2);
342 		} else {
343 			offset.x = width  * (index % 2);
344 			offset.y = height * (index / 2);
345 		}
346 		break;
347 	case 32:
348 		if (JobData::kPortrait == orientation) {
349 			offset.x = width  * (index / 4);
350 			offset.y = height * (index % 4);
351 		} else {
352 			offset.x = width  * (index % 4);
353 			offset.y = height * (index / 4);
354 		}
355 		break;
356 	case 4:
357 		offset.x = width  * (index / 2);
358 		offset.y = height * (index % 2);
359 		break;
360 	case 9:
361 		offset.x = width  * (index / 3);
362 		offset.y = height * (index % 3);
363 		break;
364 	case 16:
365 		offset.x = width  * (index / 4);
366 		offset.y = height * (index % 4);
367 		break;
368 	case 25:
369 		offset.x = width  * (index / 5);
370 		offset.y = height * (index % 5);
371 		break;
372 	case 36:
373 		offset.x = width  * (index / 6);
374 		offset.y = height * (index % 6);
375 		break;
376 	case 49:
377 		offset.x = width  * (index / 7);
378 		offset.y = height * (index % 7);
379 		break;
380 	case 64:
381 		offset.x = width  * (index / 8);
382 		offset.y = height * (index % 8);
383 		break;
384 	case 81:
385 		offset.x = width  * (index / 9);
386 		offset.y = height * (index % 9);
387 		break;
388 	case 100:
389 		offset.x = width  * (index / 10);
390 		offset.y = height * (index % 10);
391 		break;
392 	case 121:
393 		offset.x = width  * (index / 11);
394 		offset.y = height * (index % 11);
395 		break;
396 	}
397 
398 	// adjust margin
399 	offset.x += scaledPrintableRect.left - physicalRect.left;
400 	offset.y += scaledPrintableRect.top - physicalRect.top;
401 
402 	float real_scale = min(scale->x, scale->y);
403 	if (real_scale != scale->x)
404 		offset.x *= scale->x / real_scale;
405 	else
406 		offset.y *= scale->y / real_scale;
407 
408 	return offset;
409 }
410 
411 
412 // print the specified pages on a physical page
413 bool
414 GraphicsDriver::_PrintPage(PageDataList* pages)
415 {
416 	BPoint offset;
417 	offset.x = 0.0f;
418 	offset.y = 0.0f;
419 
420 	if (pages == NULL) {
421 		return true;
422 	}
423 
424 	do {
425 		// clear the physical page
426 		fView->SetScale(1.0);
427 		fView->SetHighColor(255, 255, 255);
428 		fView->ConstrainClippingRegion(NULL);
429 		fView->FillRect(fView->Bounds());
430 
431 		BPoint scale = GetScale(fOrgJobData->GetNup(),
432 			fOrgJobData->GetPhysicalRect(), fOrgJobData->GetScaling());
433 		float real_scale = min(scale.x, scale.y) * fOrgJobData->GetXres()
434 			/ 72.0f;
435 		fView->SetScale(real_scale);
436 		float x = offset.x / real_scale;
437 		float y = offset.y / real_scale;
438 		int page_index = 0;
439 
440 		for (PageDataList::iterator it = pages->begin(); it != pages->end();
441 			it++) {
442 			BPoint left_top(GetOffset(fOrgJobData->GetNup(), page_index++,
443 				fOrgJobData->GetOrientation(), &scale,
444 				fOrgJobData->GetScaledPhysicalRect(),
445 				fOrgJobData->GetScaledPrintableRect(),
446 				fOrgJobData->GetPhysicalRect()));
447 
448 			left_top.x -= x;
449 			left_top.y -= y;
450 
451 			BRect clip(fOrgJobData->GetScaledPrintableRect());
452 			clip.OffsetTo(left_top);
453 
454 			BRegion *region = new BRegion();
455 			region->Set(clip);
456 			fView->ConstrainClippingRegion(region);
457 			delete region;
458 
459 			if ((*it)->startEnum()) {
460 				bool more;
461 				do {
462 					PictureData	*picture_data;
463 					more = (*it)->enumObject(&picture_data);
464 					BPoint real_offset = left_top + picture_data->point;
465 					fView->DrawPicture(picture_data->picture, real_offset);
466 					fView->Sync();
467 					delete picture_data;
468 				} while (more);
469 			}
470 		}
471 
472 		if (!_PrintBand(fBitmap, &offset))
473 			return false;
474 
475 	} while (offset.x >= 0.0f && offset.y >= 0.0f);
476 
477 	return true;
478 }
479 
480 
481 bool
482 GraphicsDriver::_PrintBand(BBitmap* band, BPoint* offset)
483 {
484 	if (!_NeedRotateBitmapBand())
485 		return NextBand(band, offset);
486 
487 	_RotateInto(fRotatedBitmap, band);
488 
489 	BPoint rotatedOffset(offset->y, offset->x);
490 	bool success = NextBand(fRotatedBitmap, &rotatedOffset);
491 	offset->x = rotatedOffset.y;
492 	offset->y = rotatedOffset.x;
493 
494 	return success;
495 }
496 
497 
498 void
499 GraphicsDriver::_RotateInto(BBitmap* target, const BBitmap* source)
500 {
501 	ASSERT(target->ColorSpace() == B_RGB32);
502 	ASSERT(source->ColorSpace() == B_RGB32);
503 	ASSERT(target->Bounds().IntegerWidth() == source->Bounds().IntegerHeight());
504 	ASSERT(target->Bounds().IntegerHeight() == source->Bounds().IntegerWidth());
505 
506 	const int32 width = source->Bounds().IntegerWidth() + 1;
507 	const int32 height = source->Bounds().IntegerHeight() + 1;
508 
509 	const int32 sourceBPR = source->BytesPerRow();
510 	const int32 targetBPR = target->BytesPerRow();
511 
512 	const uint8_t* sourceBits =
513 		reinterpret_cast<const uint8_t*>(source->Bits());
514 	uint8_t* targetBits = static_cast<uint8_t*>(target->Bits());
515 
516 	for (int32 y = 0; y < height; y ++) {
517 		for (int32 x = 0; x < width; x ++) {
518 			const uint32_t* sourcePixel =
519 				reinterpret_cast<const uint32_t*>(sourceBits + sourceBPR * y
520 					+ sizeof(uint32_t) * x);
521 
522 			int32 targetX = (height - y - 1);
523 			int32 targetY = x;
524 			uint32_t* targetPixel =
525 				reinterpret_cast<uint32_t*>(targetBits + targetBPR * targetY
526 					+ sizeof(uint32_t) * targetX);
527 			*targetPixel = *sourcePixel;
528 		}
529 	}
530 }
531 
532 bool
533 GraphicsDriver::_CollectPages(SpoolData* spoolData, PageDataList* pages)
534 {
535 	// collect the pages to be printed on the physical page
536 	PageData *page_data;
537 	int nup = fOrgJobData->GetNup();
538 	bool more;
539 	do {
540 		more = spoolData->enumObject(&page_data);
541 		if (pages != NULL)
542 			pages->push_back(page_data);
543 	} while (more && --nup);
544 
545 	return more;
546 }
547 
548 
549 bool
550 GraphicsDriver::_SkipPages(SpoolData* spoolData)
551 {
552 	return _CollectPages(spoolData, NULL);
553 }
554 
555 
556 bool
557 GraphicsDriver::_PrintDocument(SpoolData* spoolData)
558 {
559 	bool more;
560 	bool success;
561 	int page_index;
562 	int copy;
563 	int copies;
564 
565 	more = true;
566 	success = true;
567 	page_index = 0;
568 
569 	if (fPrinterCap->Supports(PrinterCap::kCopyCommand))
570 		// let the printer perform the copy operation
571 		copies = 1;
572 	else
573 		// send the page multiple times to the printer
574 		copies = fRealJobData->GetCopies();
575 
576 	fStatusWindow -> SetPageCopies(copies);
577 		// inform fStatusWindow about number of copies
578 
579 	// printing of even/odd numbered pages only is valid in simplex mode
580 	bool simplex = fRealJobData->GetPrintStyle() == JobData::kSimplex;
581 
582 	if (spoolData->startEnum()) {
583 		do {
584 			DBGMSG(("page index = %d\n", page_index));
585 
586 			if (simplex
587 				&& fRealJobData->GetPageSelection()
588 					== JobData::kEvenNumberedPages)
589 				// skip odd numbered page
590 				more = _SkipPages(spoolData);
591 
592 			if (!more)
593 				// end reached
594 				break;
595 
596 			PageDataList pages;
597 			more = _CollectPages(spoolData, &pages);
598 
599 			if (more && simplex
600 				&& fRealJobData->GetPageSelection()
601 					== JobData::kOddNumberedPages)
602 				// skip even numbered page
603 				more = _SkipPages(spoolData);
604 
605 			// print each physical page "copies" of times
606 			for (copy = 0; success && copy < copies; copy ++) {
607 
608 				// Update the status / cancel job
609 				if (fStatusWindow->UpdateStatusBar(page_index, copy))
610 					return false;
611 
612 				success = StartPage(page_index);
613 				if (!success)
614 					break;
615 
616 				// print the pages on the physical page
617 				fView->Window()->Lock();
618 				success = _PrintPage(&pages);
619 				fView->Window()->Unlock();
620 
621 				if (success) {
622 					success = EndPage(page_index);
623 				}
624 			}
625 
626 			page_index++;
627 		} while (success && more);
628 	}
629 
630 #ifndef USE_PREVIEW_FOR_DEBUG
631 	if (success
632 		&& fPrinterCap->Supports(PrinterCap::kPrintStyle)
633 		&& (fOrgJobData->GetPrintStyle() != JobData::kSimplex)
634 		&& (((page_index + fOrgJobData->GetNup() - 1) / fOrgJobData->GetNup())
635 			% 2)) {
636 		// append an empty page on the back side of the page in duplex or
637 		// booklet mode
638 		success =
639 			StartPage(page_index) &&
640 			_PrintPage(NULL) &&
641 			EndPage(page_index);
642 	}
643 #endif
644 
645 	return success;
646 }
647 
648 
649 const JobData*
650 GraphicsDriver::GetJobData(BFile* spoolFile)
651 {
652 	_SetupData(spoolFile);
653 	return fOrgJobData;
654 }
655 
656 
657 bool
658 GraphicsDriver::_PrintJob(BFile* spoolFile)
659 {
660 	bool success = true;
661 	if (!_SetupData(spoolFile)) {
662 		// silently exit if there is nothing to print
663 		return true;
664 	}
665 
666 	fTransport = new Transport(fPrinterData);
667 
668 	if (fTransport->CheckAbort()) {
669 		success = false;
670 	} else if (!fTransport->IsPrintToFileCanceled()) {
671 		BStopWatch stopWatch("printJob", !MEASURE_PRINT_JOB_TIME);
672 		_SetupBitmap();
673 		SpoolData spoolData(spoolFile, fPageCount, fOrgJobData->GetNup(),
674 			fOrgJobData->GetReverse());
675 		success = StartDocument();
676 		if (success) {
677 			fStatusWindow = new StatusWindow(
678 				fRealJobData->GetPageSelection() == JobData::kOddNumberedPages,
679 				fRealJobData->GetPageSelection() == JobData::kEvenNumberedPages,
680 				fRealJobData->GetFirstPage(),
681 				fPageCount,
682 				fInternalCopies,fRealJobData->GetNup());
683 
684 			while (fInternalCopies--) {
685 				success = _PrintDocument(&spoolData);
686 				if (success == false) {
687 					break;
688 				}
689 			}
690 			EndDocument(success);
691 
692 			fStatusWindow->Lock();
693 			fStatusWindow->Quit();
694 		}
695 		_CleanupBitmap();
696 		_CleanupData();
697 	}
698 
699 	if (success == false) {
700 		BAlert *alert;
701 		if (fTransport->CheckAbort())
702 			alert = new BAlert("", fTransport->LastError().c_str(), "OK");
703 		else
704 			alert = new BAlert("", "Printer not responding.", "OK");
705 		alert->Go();
706 	}
707 
708 	delete fTransport;
709 	fTransport = NULL;
710 
711 	return success;
712 }
713 
714 
715 BMessage*
716 GraphicsDriver::TakeJob(BFile* spoolFile)
717 {
718 	BMessage *msg;
719 	if (_PrintJob(spoolFile))
720 		msg = new BMessage('okok');
721 	else
722 		msg = new BMessage('baad');
723 	return msg;
724 }
725 
726 
727 bool
728 GraphicsDriver::StartDocument()
729 {
730 	return true;
731 }
732 
733 
734 bool
735 GraphicsDriver::StartPage(int)
736 {
737 	return true;
738 }
739 
740 
741 bool
742 GraphicsDriver::NextBand(BBitmap*, BPoint*)
743 {
744 	return true;
745 }
746 
747 
748 bool
749 GraphicsDriver::EndPage(int)
750 {
751 	return true;
752 }
753 
754 
755 bool
756 GraphicsDriver::EndDocument(bool)
757 {
758 	return true;
759 }
760 
761 
762 void
763 GraphicsDriver::WriteSpoolData(const void* buffer, size_t size)
764 	throw (TransportException)
765 {
766 	if (fTransport == NULL)
767 		return;
768 	fTransport->Write(buffer, size);
769 }
770 
771 
772 void
773 GraphicsDriver::WriteSpoolString(const char* format, ...)
774 	throw (TransportException)
775 {
776 	if (fTransport == NULL)
777 		return;
778 
779 	char buffer[256];
780 	va_list	ap;
781 	va_start(ap, format);
782 	vsprintf(buffer, format, ap);
783 	fTransport->Write(buffer, strlen(buffer));
784 	va_end(ap);
785 }
786 
787 
788 void
789 GraphicsDriver::WriteSpoolChar(char c)
790 	throw (TransportException)
791 {
792 	if (fTransport == NULL)
793 		return;
794 
795 	fTransport->Write(&c, 1);
796 }
797 
798 
799 bool
800 GraphicsDriver::_NeedRotateBitmapBand() const
801 {
802 	return JobData::kLandscape == fRealJobData->GetOrientation()
803 		&& !fPrinterCap->Supports(PrinterCap::kCanRotatePageInLandscape);
804 }
805 
806 
807 void
808 GraphicsDriver::_ConvertRGB32ToRGB24(const void* src, void* dst, int width) {
809 	uint8* d = (uint8*)dst;
810 	const rgb_color* s = static_cast<const rgb_color*>(src);
811 	for (int i = width; i > 0; i --) {
812 		*d ++ = s->red;
813 		*d ++ = s->green;
814 		*d ++ = s->blue;
815 		s++;
816 	}
817 }
818 
819 
820 void
821 GraphicsDriver::_ConvertCMAP8ToRGB24(const void* src, void* dst, int width) {
822 	uint8* d = (uint8*)dst;
823 	const uint8* s = static_cast<const uint8*>(src);
824 	const color_map* cmap = system_colors();
825 	for (int i = width; i > 0; i --) {
826 		const rgb_color* rgb = &cmap->color_list[*s];
827 		*d ++ = rgb->red;
828 		*d ++ = rgb->green;
829 		*d ++ = rgb->blue;
830 		s ++;
831 	}
832 }
833 
834 
835 void
836 GraphicsDriver::ConvertToRGB24(const void* src, void* dst, int width,
837 	color_space cs) {
838 	if (cs == B_RGB32)
839 		_ConvertRGB32ToRGB24(src, dst, width);
840 	else if (cs == B_CMAP8)
841 		_ConvertCMAP8ToRGB24(src, dst, width);
842 	else {
843 		DBGMSG(("color_space %d not supported", cs));
844 	}
845 }
846 
847 
848 uint8
849 GraphicsDriver::_ConvertToGray(uint8 r, uint8 g, uint8 b) {
850 	if (r == g && g == b)
851 		return r;
852 	else
853 		return (r * 3 + g * 6 + b) / 10;
854 }
855 
856 
857 void
858 GraphicsDriver::_ConvertRGB32ToGray(const void* src, void* dst, int width) {
859 	uint8* d = (uint8*)dst;
860 	const rgb_color* s = static_cast<const rgb_color*>(src);
861 	for (int i = width; i > 0; i--, s++, d++)
862 		*d = _ConvertToGray(s->red, s->green, s->blue);
863 }
864 
865 
866 void
867 GraphicsDriver::_ConvertCMAP8ToGray(const void* src, void* dst, int width) {
868 	uint8* d = (uint8*)dst;
869 	const uint8* s = static_cast<const uint8*>(src);
870 	const color_map* cmap = system_colors();
871 	for (int i = width; i > 0; i--, s++, d++) {
872 		const rgb_color* rgb = &cmap->color_list[*s];
873 		*d = _ConvertToGray(rgb->red, rgb->green, rgb->blue);
874 	}
875 }
876 
877 
878 void
879 GraphicsDriver::ConvertToGray(const void* src, void* dst, int width,
880 	color_space cs) {
881 	if (cs == B_RGB32)
882 		_ConvertRGB32ToGray(src, dst, width);
883 	else if (cs == B_CMAP8)
884 		_ConvertCMAP8ToGray(src, dst, width);
885 	else {
886 		DBGMSG(("color_space %d not supported", cs));
887 	}
888 }
889 
890