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