/* * GraphicsDriver.cpp * Copyright 1999-2000 Y.Takagi. All Rights Reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GraphicsDriver.h" #include "PrintProcess.h" #include "JobData.h" #include "PrinterData.h" #include "PrinterCap.h" #include "Preview.h" #include "Transport.h" #include "ValidRect.h" #include "DbgMsg.h" using namespace std; // Measure printJob() time. Either true or false. #define MEASURE_PRINT_JOB_TIME false GraphicsDriver::GraphicsDriver(BMessage* message, PrinterData* printerData, const PrinterCap* printerCap) : fMessage(message), fView(NULL), fBitmap(NULL), fRotatedBitmap(NULL), fTransport(NULL), fOrgJobData(NULL), fRealJobData(NULL), fPrinterData(printerData), fPrinterCap(printerCap), fSpoolMetaData(NULL), fPageWidth(0), fPageHeight(0), fBandWidth(0), fBandHeight(0), fPixelDepth(0), fBandCount(0), fInternalCopies(0), fPageCount(0), fStatusWindow(NULL) { } GraphicsDriver::~GraphicsDriver() { } static BRect RotateRect(BRect rect) { BRect rotated(rect.top, rect.left, rect.bottom, rect.right); return rotated; } bool GraphicsDriver::_SetupData(BFile* spoolFile) { if (fOrgJobData != NULL) { // already initialized return true; } print_file_header pfh; spoolFile->Seek(0, SEEK_SET); spoolFile->Read(&pfh, sizeof(pfh)); DBGMSG(("print_file_header::version = 0x%x\n", pfh.version)); DBGMSG(("print_file_header::page_count = %d\n", pfh.page_count)); DBGMSG(("print_file_header::first_page = 0x%x\n", (int)pfh.first_page)); if (pfh.page_count <= 0) { // nothing to print return false; } fPageCount = (uint32) pfh.page_count; BMessage *msg = new BMessage(); msg->Unflatten(spoolFile); fOrgJobData = new JobData(msg, fPrinterCap, JobData::kJobSettings); DUMP_BMESSAGE(msg); delete msg; fRealJobData = new JobData(*fOrgJobData); switch (fOrgJobData->GetNup()) { case 2: case 8: case 32: case 128: fRealJobData->SetPrintableRect( RotateRect(fOrgJobData->GetPrintableRect())); fRealJobData->SetScaledPrintableRect( RotateRect(fOrgJobData->GetScaledPrintableRect())); fRealJobData->SetPhysicalRect( RotateRect(fOrgJobData->GetPhysicalRect())); fRealJobData->SetScaledPhysicalRect( RotateRect(fOrgJobData->GetScaledPhysicalRect())); if (JobData::kPortrait == fOrgJobData->GetOrientation()) fRealJobData->SetOrientation(JobData::kLandscape); else fRealJobData->SetOrientation(JobData::kPortrait); break; } if (fOrgJobData->GetCollate() && fPageCount > 1) { fRealJobData->SetCopies(1); fInternalCopies = fOrgJobData->GetCopies(); } else { fInternalCopies = 1; } fSpoolMetaData = new SpoolMetaData(spoolFile); return true; } void GraphicsDriver::_CleanupData() { delete fRealJobData; delete fOrgJobData; delete fSpoolMetaData; fRealJobData = NULL; fOrgJobData = NULL; fSpoolMetaData = NULL; } void GraphicsDriver::_SetupBitmap() { fPixelDepth = color_space2pixel_depth(fOrgJobData->GetSurfaceType()); fPageWidth = (fRealJobData->GetPhysicalRect().IntegerWidth() * fOrgJobData->GetXres() + 71) / 72; fPageHeight = (fRealJobData->GetPhysicalRect().IntegerHeight() * fOrgJobData->GetYres() + 71) / 72; fBitmap = NULL; fRotatedBitmap = NULL; BRect rect; for (fBandCount = 1; fBandCount < 256; fBandCount++) { if (_NeedRotateBitmapBand()) { fBandWidth = (fPageWidth + fBandCount - 1) / fBandCount; fBandHeight = fPageHeight; } else { fBandWidth = fPageWidth; fBandHeight = (fPageHeight + fBandCount - 1) / fBandCount; } rect.Set(0, 0, fBandWidth - 1, fBandHeight - 1); fBitmap = new(std::nothrow) BBitmap(rect, fOrgJobData->GetSurfaceType(), true); if (fBitmap == NULL || fBitmap->InitCheck() != B_OK) { delete fBitmap; fBitmap = NULL; // Try with smaller bands continue; } if (_NeedRotateBitmapBand()) { BRect rotatedRect(0, 0, rect.bottom, rect.right); delete fRotatedBitmap; fRotatedBitmap = new(std::nothrow) BBitmap(rotatedRect, fOrgJobData->GetSurfaceType(), false); if (fRotatedBitmap == NULL || fRotatedBitmap->InitCheck() != B_OK) { delete fBitmap; fBitmap = NULL; delete fRotatedBitmap; fRotatedBitmap = NULL; // Try with smaller bands continue; } } // If we get here, all needed allocations have succeeded, we can safely // go ahead. break; }; if (fBitmap == NULL) { debugger("Failed to allocate bitmaps for print rasterization"); return; } fView = new BView(rect, "", B_FOLLOW_ALL, B_WILL_DRAW); fBitmap->AddChild(fView); DBGMSG(("****************\n")); DBGMSG(("page_width = %d\n", fPageWidth)); DBGMSG(("page_height = %d\n", fPageHeight)); DBGMSG(("band_count = %d\n", fBandCount)); DBGMSG(("band_height = %d\n", fBandHeight)); DBGMSG(("****************\n")); } void GraphicsDriver::_CleanupBitmap() { delete fBitmap; fBitmap = NULL; fView = NULL; delete fRotatedBitmap; fRotatedBitmap = NULL; } BPoint GraphicsDriver::GetScale(int32 nup, BRect physicalRect, float scaling) { float width; float height; BPoint scale; scale.x = scale.y = 1.0f; switch (nup) { case 1: scale.x = scale.y = 1.0f; break; case 2: /* 1x2 or 2x1 */ width = physicalRect.Width(); height = physicalRect.Height(); if (width < height) { // portrait scale.x = height / 2.0f / width; scale.y = width / height; } else { // landscape scale.x = height / width; scale.y = width / 2.0f / height; } break; case 8: /* 2x4 or 4x2 */ width = physicalRect.Width(); height = physicalRect.Height(); if (width < height) { scale.x = height / 4.0f / width; scale.y = width / height / 2.0f; } else { scale.x = height / width / 2.0f; scale.y = width / 4.0f / height; } break; case 32: /* 4x8 or 8x4 */ width = physicalRect.Width(); height = physicalRect.Height(); if (width < height) { scale.x = height / 8.0f / width; scale.y = width / height / 4.0f; } else { scale.x = height / width / 4.0f; scale.y = width / 8.0f / height; } break; case 4: /* 2x2 */ scale.x = scale.y = 1.0f / 2.0f; break; case 9: /* 3x3 */ scale.x = scale.y = 1.0f / 3.0f; break; case 16: /* 4x4 */ scale.x = scale.y = 1.0f / 4.0f; break; case 25: /* 5x5 */ scale.x = scale.y = 1.0f / 5.0f; break; case 36: /* 6x6 */ scale.x = scale.y = 1.0f / 6.0f; break; case 49: /* 7x7 */ scale.x = scale.y = 1.0f / 7.0f; break; case 64: /* 8x8 */ scale.x = scale.y = 1.0f / 8.0f; break; case 81: /* 9x9 */ scale.x = scale.y = 1.0f / 9.0f; break; case 100: /* 10x10 */ scale.x = scale.y = 1.0f / 10.0f; break; case 121: /* 11x11 */ scale.x = scale.y = 1.0f / 11.0f; break; } scale.x = scale.x * scaling / 100.0; scale.y = scale.y * scaling / 100.0; return scale; } BPoint GraphicsDriver::GetOffset(int32 nup, int index, JobData::Orientation orientation, const BPoint* scale, BRect scaledPhysicalRect, BRect scaledPrintableRect, BRect physicalRect) { BPoint offset; offset.x = 0; offset.y = 0; float width = scaledPhysicalRect.Width(); float height = scaledPhysicalRect.Height(); switch (nup) { case 1: break; case 2: if (index == 1) { if (JobData::kPortrait == orientation) { offset.x = width; } else { offset.y = height; } } break; case 8: if (JobData::kPortrait == orientation) { offset.x = width * (index / 2); offset.y = height * (index % 2); } else { offset.x = width * (index % 2); offset.y = height * (index / 2); } break; case 32: if (JobData::kPortrait == orientation) { offset.x = width * (index / 4); offset.y = height * (index % 4); } else { offset.x = width * (index % 4); offset.y = height * (index / 4); } break; case 4: offset.x = width * (index / 2); offset.y = height * (index % 2); break; case 9: offset.x = width * (index / 3); offset.y = height * (index % 3); break; case 16: offset.x = width * (index / 4); offset.y = height * (index % 4); break; case 25: offset.x = width * (index / 5); offset.y = height * (index % 5); break; case 36: offset.x = width * (index / 6); offset.y = height * (index % 6); break; case 49: offset.x = width * (index / 7); offset.y = height * (index % 7); break; case 64: offset.x = width * (index / 8); offset.y = height * (index % 8); break; case 81: offset.x = width * (index / 9); offset.y = height * (index % 9); break; case 100: offset.x = width * (index / 10); offset.y = height * (index % 10); break; case 121: offset.x = width * (index / 11); offset.y = height * (index % 11); break; } // adjust margin offset.x += scaledPrintableRect.left - physicalRect.left; offset.y += scaledPrintableRect.top - physicalRect.top; float real_scale = min(scale->x, scale->y); if (real_scale != scale->x) offset.x *= scale->x / real_scale; else offset.y *= scale->y / real_scale; return offset; } // print the specified pages on a physical page bool GraphicsDriver::_PrintPage(PageDataList* pages) { BPoint offset; offset.x = 0.0f; offset.y = 0.0f; if (pages == NULL) { return true; } do { // clear the physical page fView->SetScale(1.0); fView->SetHighColor(255, 255, 255); fView->ConstrainClippingRegion(NULL); fView->FillRect(fView->Bounds()); BPoint scale = GetScale(fOrgJobData->GetNup(), fOrgJobData->GetPhysicalRect(), fOrgJobData->GetScaling()); float real_scale = min(scale.x, scale.y) * fOrgJobData->GetXres() / 72.0f; fView->SetScale(real_scale); float x = offset.x / real_scale; float y = offset.y / real_scale; int page_index = 0; for (PageDataList::iterator it = pages->begin(); it != pages->end(); it++) { BPoint left_top(GetOffset(fOrgJobData->GetNup(), page_index++, fOrgJobData->GetOrientation(), &scale, fOrgJobData->GetScaledPhysicalRect(), fOrgJobData->GetScaledPrintableRect(), fOrgJobData->GetPhysicalRect())); left_top.x -= x; left_top.y -= y; BRect clip(fOrgJobData->GetScaledPrintableRect()); clip.OffsetTo(left_top); BRegion *region = new BRegion(); region->Set(clip); fView->ConstrainClippingRegion(region); delete region; if ((*it)->startEnum()) { bool more; do { PictureData *picture_data; more = (*it)->enumObject(&picture_data); BPoint real_offset = left_top + picture_data->point; fView->DrawPicture(picture_data->picture, real_offset); fView->Sync(); delete picture_data; } while (more); } } if (!_PrintBand(fBitmap, &offset)) return false; } while (offset.x >= 0.0f && offset.y >= 0.0f); return true; } bool GraphicsDriver::_PrintBand(BBitmap* band, BPoint* offset) { if (!_NeedRotateBitmapBand()) return NextBand(band, offset); _RotateInto(fRotatedBitmap, band); BPoint rotatedOffset(offset->y, offset->x); bool success = NextBand(fRotatedBitmap, &rotatedOffset); offset->x = rotatedOffset.y; offset->y = rotatedOffset.x; return success; } void GraphicsDriver::_RotateInto(BBitmap* target, const BBitmap* source) { ASSERT(target->ColorSpace() == B_RGB32); ASSERT(source->ColorSpace() == B_RGB32); ASSERT(target->Bounds().IntegerWidth() == source->Bounds().IntegerHeight()); ASSERT(target->Bounds().IntegerHeight() == source->Bounds().IntegerWidth()); const int32 width = source->Bounds().IntegerWidth() + 1; const int32 height = source->Bounds().IntegerHeight() + 1; const int32 sourceBPR = source->BytesPerRow(); const int32 targetBPR = target->BytesPerRow(); const uint8_t* sourceBits = reinterpret_cast(source->Bits()); uint8_t* targetBits = static_cast(target->Bits()); for (int32 y = 0; y < height; y ++) { for (int32 x = 0; x < width; x ++) { const uint32_t* sourcePixel = reinterpret_cast(sourceBits + sourceBPR * y + sizeof(uint32_t) * x); int32 targetX = (height - y - 1); int32 targetY = x; uint32_t* targetPixel = reinterpret_cast(targetBits + targetBPR * targetY + sizeof(uint32_t) * targetX); *targetPixel = *sourcePixel; } } } bool GraphicsDriver::_CollectPages(SpoolData* spoolData, PageDataList* pages) { // collect the pages to be printed on the physical page PageData *page_data; int nup = fOrgJobData->GetNup(); bool more; do { more = spoolData->enumObject(&page_data); if (pages != NULL) pages->push_back(page_data); } while (more && --nup); return more; } bool GraphicsDriver::_SkipPages(SpoolData* spoolData) { return _CollectPages(spoolData, NULL); } bool GraphicsDriver::_PrintDocument(SpoolData* spoolData) { bool more; bool success; int page_index; int copy; int copies; more = true; success = true; page_index = 0; if (fPrinterCap->Supports(PrinterCap::kCopyCommand)) // let the printer perform the copy operation copies = 1; else // send the page multiple times to the printer copies = fRealJobData->GetCopies(); fStatusWindow -> SetPageCopies(copies); // inform fStatusWindow about number of copies // printing of even/odd numbered pages only is valid in simplex mode bool simplex = fRealJobData->GetPrintStyle() == JobData::kSimplex; if (spoolData->startEnum()) { do { DBGMSG(("page index = %d\n", page_index)); if (simplex && fRealJobData->GetPageSelection() == JobData::kEvenNumberedPages) // skip odd numbered page more = _SkipPages(spoolData); if (!more) // end reached break; PageDataList pages; more = _CollectPages(spoolData, &pages); if (more && simplex && fRealJobData->GetPageSelection() == JobData::kOddNumberedPages) // skip even numbered page more = _SkipPages(spoolData); // print each physical page "copies" of times for (copy = 0; success && copy < copies; copy ++) { // Update the status / cancel job if (fStatusWindow->UpdateStatusBar(page_index, copy)) return false; success = StartPage(page_index); if (!success) break; // print the pages on the physical page fView->Window()->Lock(); success = _PrintPage(&pages); fView->Window()->Unlock(); if (success) { success = EndPage(page_index); } } page_index++; } while (success && more); } #ifndef USE_PREVIEW_FOR_DEBUG if (success && fPrinterCap->Supports(PrinterCap::kPrintStyle) && (fOrgJobData->GetPrintStyle() != JobData::kSimplex) && (((page_index + fOrgJobData->GetNup() - 1) / fOrgJobData->GetNup()) % 2)) { // append an empty page on the back side of the page in duplex or // booklet mode success = StartPage(page_index) && _PrintPage(NULL) && EndPage(page_index); } #endif return success; } const JobData* GraphicsDriver::GetJobData(BFile* spoolFile) { _SetupData(spoolFile); return fOrgJobData; } bool GraphicsDriver::_PrintJob(BFile* spoolFile) { bool success = true; if (!_SetupData(spoolFile)) { // silently exit if there is nothing to print return true; } fTransport = new Transport(fPrinterData); if (fTransport->CheckAbort()) { success = false; } else if (!fTransport->IsPrintToFileCanceled()) { BStopWatch stopWatch("printJob", !MEASURE_PRINT_JOB_TIME); _SetupBitmap(); SpoolData spoolData(spoolFile, fPageCount, fOrgJobData->GetNup(), fOrgJobData->GetReverse()); success = StartDocument(); if (success) { fStatusWindow = new StatusWindow( fRealJobData->GetPageSelection() == JobData::kOddNumberedPages, fRealJobData->GetPageSelection() == JobData::kEvenNumberedPages, fRealJobData->GetFirstPage(), fPageCount, fInternalCopies,fRealJobData->GetNup()); while (fInternalCopies--) { success = _PrintDocument(&spoolData); if (success == false) { break; } } EndDocument(success); fStatusWindow->Lock(); fStatusWindow->Quit(); } _CleanupBitmap(); _CleanupData(); } if (success == false) { BAlert *alert; if (fTransport->CheckAbort()) alert = new BAlert("", fTransport->LastError().c_str(), "OK"); else alert = new BAlert("", "Printer not responding.", "OK"); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); } delete fTransport; fTransport = NULL; return success; } BMessage* GraphicsDriver::TakeJob(BFile* spoolFile) { BMessage *msg; if (_PrintJob(spoolFile)) msg = new BMessage('okok'); else msg = new BMessage('baad'); return msg; } bool GraphicsDriver::StartDocument() { return true; } bool GraphicsDriver::StartPage(int) { return true; } bool GraphicsDriver::NextBand(BBitmap*, BPoint*) { return true; } bool GraphicsDriver::EndPage(int) { return true; } bool GraphicsDriver::EndDocument(bool) { return true; } void GraphicsDriver::WriteSpoolData(const void* buffer, size_t size) { if (fTransport == NULL) return; fTransport->Write(buffer, size); } void GraphicsDriver::WriteSpoolString(const char* format, ...) { if (fTransport == NULL) return; char buffer[256]; va_list ap; va_start(ap, format); vsprintf(buffer, format, ap); fTransport->Write(buffer, strlen(buffer)); va_end(ap); } void GraphicsDriver::WriteSpoolChar(char c) { if (fTransport == NULL) return; fTransport->Write(&c, 1); } void GraphicsDriver::ReadSpoolData(void* buffer, size_t size) { if (fTransport == NULL) return; fTransport->Read(buffer, size); } int GraphicsDriver::ReadSpoolChar() { if (fTransport == NULL) return -1; char c; fTransport->Read(&c, 1); return c; } bool GraphicsDriver::_NeedRotateBitmapBand() const { return JobData::kLandscape == fRealJobData->GetOrientation() && !fPrinterCap->Supports(PrinterCap::kCanRotatePageInLandscape); } void GraphicsDriver::_ConvertRGB32ToRGB24(const void* src, void* dst, int width) { uint8* d = (uint8*)dst; const rgb_color* s = static_cast(src); for (int i = width; i > 0; i --) { *d ++ = s->red; *d ++ = s->green; *d ++ = s->blue; s++; } } void GraphicsDriver::_ConvertCMAP8ToRGB24(const void* src, void* dst, int width) { uint8* d = (uint8*)dst; const uint8* s = static_cast(src); const color_map* cmap = system_colors(); for (int i = width; i > 0; i --) { const rgb_color* rgb = &cmap->color_list[*s]; *d ++ = rgb->red; *d ++ = rgb->green; *d ++ = rgb->blue; s ++; } } void GraphicsDriver::ConvertToRGB24(const void* src, void* dst, int width, color_space cs) { if (cs == B_RGB32) _ConvertRGB32ToRGB24(src, dst, width); else if (cs == B_CMAP8) _ConvertCMAP8ToRGB24(src, dst, width); else { DBGMSG(("color_space %d not supported", cs)); } } uint8 GraphicsDriver::_ConvertToGray(uint8 r, uint8 g, uint8 b) { if (r == g && g == b) return r; else return (r * 3 + g * 6 + b) / 10; } void GraphicsDriver::_ConvertRGB32ToGray(const void* src, void* dst, int width) { uint8* d = (uint8*)dst; const rgb_color* s = static_cast(src); for (int i = width; i > 0; i--, s++, d++) *d = _ConvertToGray(s->red, s->green, s->blue); } void GraphicsDriver::_ConvertCMAP8ToGray(const void* src, void* dst, int width) { uint8* d = (uint8*)dst; const uint8* s = static_cast(src); const color_map* cmap = system_colors(); for (int i = width; i > 0; i--, s++, d++) { const rgb_color* rgb = &cmap->color_list[*s]; *d = _ConvertToGray(rgb->red, rgb->green, rgb->blue); } } void GraphicsDriver::ConvertToGray(const void* src, void* dst, int width, color_space cs) { if (cs == B_RGB32) _ConvertRGB32ToGray(src, dst, width); else if (cs == B_CMAP8) _ConvertCMAP8ToGray(src, dst, width); else { DBGMSG(("color_space %d not supported", cs)); } }