/*
* GraphicsDriver.cpp
* Copyright 1999-2000 Y.Takagi. All Rights Reserved.
*/
#include <algorithm>
#include <cstdio>
#include <cstdarg>
#include <Alert.h>
#include <Bitmap.h>
#include <Debug.h>
#include <Message.h>
#include <PrintJob.h>
#include <Region.h>
#include <TextControl.h>
#include <TextControl.h>
#include <StopWatch.h>
#include <View.h>
#include <Directory.h>
#include <File.h>
#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
enum {
kMaxMemorySize = 4 * 1024 * 1024
};
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;
int widthByte = (fPageWidth * fPixelDepth + 7) / 8;
int size = widthByte * fPageHeight;
#ifdef USE_PREVIEW_FOR_DEBUG
size = 0;
#endif
if (size < kMaxMemorySize) {
fBandCount = 0;
fBandWidth = fPageWidth;
fBandHeight = fPageHeight;
} else {
fBandCount = (size + kMaxMemorySize - 1) / kMaxMemorySize;
if (_NeedRotateBitmapBand()) {
fBandWidth = (fPageWidth + fBandCount - 1) / fBandCount;
fBandHeight = fPageHeight;
} else {
fBandWidth = fPageWidth;
fBandHeight = (fPageHeight + fBandCount - 1) / fBandCount;
}
}
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"));
BRect rect;
rect.Set(0, 0, fBandWidth - 1, fBandHeight - 1);
fBitmap = new BBitmap(rect, fOrgJobData->GetSurfaceType(), true);
fView = new BView(rect, "", B_FOLLOW_ALL, B_WILL_DRAW);
fBitmap->AddChild(fView);
if (_NeedRotateBitmapBand()) {
BRect rotatedRect(0, 0, rect.bottom, rect.right);
fRotatedBitmap = new BBitmap(rotatedRect, fOrgJobData->GetSurfaceType(),
false);
}
}
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<const uint8_t*>(source->Bits());
uint8_t* targetBits = static_cast<uint8_t*>(target->Bits());
for (int32 y = 0; y < height; y ++) {
for (int32 x = 0; x < width; x ++) {
const uint32_t* sourcePixel =
reinterpret_cast<const uint32_t*>(sourceBits + sourceBPR * y
+ sizeof(uint32_t) * x);
int32 targetX = (height - y - 1);
int32 targetY = x;
uint32_t* targetPixel =
reinterpret_cast<uint32_t*>(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<const rgb_color*>(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<const uint8*>(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<const rgb_color*>(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<const uint8*>(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));
}
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.