From d6d2c9b16db8d2f996cc7b692badfa992de60b60 Mon Sep 17 00:00:00 2001 From: Michael Pfeiffer Date: Sun, 5 Dec 2010 11:13:07 +0000 Subject: [PATCH] * The bitmap bands from libprint are relative to the imageable area; made them absolute to the left, top of the page. The page contents should now be positioned correctly. * Make sure the image to be printed on the page is inside the imageable area. This fixes the configuration error that the page is outside the imageable area. TODO fix the implementation limitation that up to 1 Inch of the total imageable page width and height cannot be used. There is a comment in the source code that explains why that is. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39733 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../print/drivers/gutenprint/GPJob.cpp | 286 ++++++++++++------ src/add-ons/print/drivers/gutenprint/GPJob.h | 11 +- .../print/drivers/gutenprint/Rectangle.h | 79 +++++ 3 files changed, 274 insertions(+), 102 deletions(-) create mode 100644 src/add-ons/print/drivers/gutenprint/Rectangle.h diff --git a/src/add-ons/print/drivers/gutenprint/GPJob.cpp b/src/add-ons/print/drivers/gutenprint/GPJob.cpp index 0d79ca2735..11b3a26963 100644 --- a/src/add-ons/print/drivers/gutenprint/GPJob.cpp +++ b/src/add-ons/print/drivers/gutenprint/GPJob.cpp @@ -10,6 +10,67 @@ #include +// 72 DPI +static const int32 kGutenprintUnit = 72; + +class CoordinateSystem +{ +public: + CoordinateSystem() + : + fXDPI(0), + fYDPI(0) + { + } + + + void SetDPI(int32 x, int32 y) { + fXDPI = x; + fYDPI = y; + } + + + void ToGutenprint(int32 fromX, int32 fromY, int32& toX, int32& toY) { + toX = fromX * kGutenprintUnit / fXDPI; + toY = fromY * kGutenprintUnit / fYDPI; + } + + + void ToGutenprintCeiling(int32 fromX, int32 fromY, int32& toX, int32& toY) { + toX = (fromX * kGutenprintUnit + fXDPI - 1) / fXDPI; + toY = (fromY * kGutenprintUnit + fYDPI - 1) / fYDPI; + } + + + void FromGutenprint(int32 fromX, int32 fromY, int32& toX, int32& toY) { + toX = fromX * fXDPI / kGutenprintUnit; + toY = fromY * fYDPI / kGutenprintUnit; + } + + void FromGutenprintCeiling(int32 fromX, int32 fromY, int32& toX, int32& toY) { + toX = (fromX * fXDPI + kGutenprintUnit - 1) / kGutenprintUnit; + toY = (fromY * fYDPI + kGutenprintUnit - 1) / kGutenprintUnit; + } + + void SizeFromGutenprint(int32 fromWidth, int32 fromHeight, + int32& toWidth, int32& toHeight) { + toWidth = fromWidth * fXDPI / kGutenprintUnit; + toHeight = fromHeight * fYDPI / kGutenprintUnit; + } + + void RoundUpToWholeInches(int32& width, int32& height) { + width = ((width + kGutenprintUnit - 1) / kGutenprintUnit) + * kGutenprintUnit; + height = ((height + kGutenprintUnit - 1) / kGutenprintUnit) + * kGutenprintUnit; + } + +private: + int32 fXDPI; + int32 fYDPI; +}; + + GPJob::GPJob() : fApplicationName(), @@ -169,130 +230,150 @@ GPJob::End() fVariables = NULL; } - -static int -gcd(int a, int b) -{ - // Euclidean algorithm for greatest common divisor - while (b != 0) { - int t = b; - b = a % b; - a = t; - } - return a; -} - - -static int -to72dpiFloor(int value, int fromUnit) { - // proper rounding is important in this formula - // do not "optimize" - const int toUnit = 72; - int g = gcd(toUnit, fromUnit); - int n = toUnit / g; - int m = fromUnit / g; - return (value / m) * n; -} - - -static int -to72dpiCeiling(int value, int fromUnit) { - // proper rounding is important in this formula - // do not "optimize" - const int toUnit = 72; - int g = gcd(toUnit, fromUnit); - int n = toUnit / g; - int m = fromUnit / g; - return ((value + m - 1) / m) * n; -} - - -static int -from72dpi(int value, int toUnit) -{ - const int fromUnit = 72; - return value * toUnit / fromUnit; -} - - status_t GPJob::PrintPage(list& bands) { if (fStatus != B_OK) return fStatus; - fPrintRect = GetPrintRectangle(bands); fBands = &bands; fCachedBand = NULL; - { - int left; - int top; - int right; - int bottom; - stp_get_imageable_area(fVariables, &left, &right, &bottom, &top); - fprintf(stderr, "GPJob imageable area left %d, top %d, right %d, " - "bottom %d\n", - left, top, right, bottom); + Rectangle imageableArea; + stp_get_imageable_area(fVariables, &imageableArea.left, + &imageableArea.right, &imageableArea.bottom, &imageableArea.top); + fprintf(stderr, "GPJob imageable area left %d, top %d, right %d, " + "bottom %d\n", + imageableArea.left, imageableArea.top, imageableArea.right, + imageableArea.bottom); + fprintf(stderr, "GPJob width %d %s, height %d %s\n", + imageableArea.Width(), + imageableArea.Width() % 72 == 0 ? "whole inches" : "not whole inches", + imageableArea.Height(), + imageableArea.Height() % 72 == 0 ? "whole inches" : "not whole inches" + ); + CoordinateSystem coordinateSystem; + coordinateSystem.SetDPI(fConfiguration->fXDPI, fConfiguration->fYDPI); + { + // GPBand offset is relative to imageable area left, top + // but it has to be absolute to left, top of page + int32 offsetX; + int32 offsetY; + coordinateSystem.FromGutenprintCeiling(imageableArea.left, + imageableArea.top, offsetX, offsetY); + + BPoint offset(offsetX, offsetY); + list::iterator it = fBands->begin(); + for (; it != fBands->end(); it++) { + (*it)->fWhere += offset; + } } + fPrintRect = GetPrintRectangle(bands); + { int left = (int)fPrintRect.left; int top = (int)fPrintRect.top; - int width = fPrintRect.IntegerWidth() + 1; - int height = fPrintRect.IntegerHeight() + 1; + int width = fPrintRect.Width() + 1; + int height = fPrintRect.Height() + 1; - fprintf(stderr, "GPJob raw image dimensions left %d, top %d, width %d, height %d\n", + fprintf(stderr, "GPJob bitmap bands frame left %d, top %d, width %d, " + "height %d\n", left, top, width, height); } - int xDPI = fConfiguration->fXDPI; - int yDPI = fConfiguration->fYDPI; + // calculate the position and size of the image to be printed on the page + // unit: 1/72 Inches + // constraints: the image must be inside the imageable area + int32 left; + int32 top; + coordinateSystem.ToGutenprint(fPrintRect.left, fPrintRect.top, left, top); + if (left < imageableArea.left) + left = imageableArea.left; + if (top < imageableArea.top) + top = imageableArea.top; - // left, top of the image on the page in 1/72 Inches - int left = static_cast(fPrintRect.left); - left = to72dpiFloor(left, xDPI); - int top = static_cast(fPrintRect.top); - top = to72dpiFloor(top, yDPI); + int32 right; + int32 bottom; + coordinateSystem.ToGutenprintCeiling(fPrintRect.right, fPrintRect.bottom, + right, bottom); + if (right > imageableArea.right) + right = imageableArea.right; + if (bottom > imageableArea.bottom) + bottom = imageableArea.bottom; - // because of rounding in the previous step, - // now the image left, top has to be synchronized - fPrintRect.left = from72dpi(left, xDPI); - fPrintRect.top = from72dpi(top, yDPI); + fprintf(stderr, "GPJob image left %d, top %d, right %d, bottom %d\n", + (int)left, (int)top, (int)right, (int)bottom); - // width and height of the image on the page in 1/72 Inches - int width = fPrintRect.IntegerWidth() + 1; - width = to72dpiCeiling(width, xDPI); - int height = fPrintRect.IntegerHeight() + 1; - height = to72dpiCeiling(height, yDPI); + // make sure the width and height in pixels is a whole number + // by increasing the width and height in 1/72 Inches to a multiple of 72 + // + // TODO the "whole number" condition has to be dropped; in the current + // implementation up to 1 Inch of the total width or height cannot be + // used; using the gcd of 72 and x or y DPI this could be reduced; + // for example if DPI is 600 to 1/3 Inch which is still not acceptable. + // + // the position might have to be changed in order to stay inside + // the imageable area; if it gets too large it is decreased by one Inch + int32 width = right - left; + int32 height = bottom - top; + coordinateSystem.RoundUpToWholeInches(width, height); + right = left + width; + bottom = top + height; - // synchronize image right and bottom too - fPrintRect.right = fPrintRect.left + from72dpi(width, xDPI); - fPrintRect.bottom = fPrintRect.top + from72dpi(height, yDPI); + // again make sure the image is inside the imageable area + if (right > imageableArea.right) { + right = imageableArea.right; + left = right - width; + if (left < imageableArea.left) { + left = imageableArea.left; + right = left + width - kGutenprintUnit; + } + width = right - left; + } + if (bottom > imageableArea.bottom) { + bottom = imageableArea.bottom; + top = bottom - height; + if (top < imageableArea.top) { + top = imageableArea.top; + bottom = top + height - kGutenprintUnit; + } + height = bottom - top; + } + + // because of rounding and clipping in the previous step, + // now the image frame has to be synchronized + coordinateSystem.FromGutenprint(left, top, fPrintRect.left, fPrintRect.top); + int32 printRectWidth; + int32 printRectHeight; + coordinateSystem.SizeFromGutenprint(width, height, printRectWidth, + printRectHeight); + fPrintRect.right = fPrintRect.left + printRectWidth - 1; + fPrintRect.bottom = fPrintRect.top + printRectHeight - 1; { - int left = (int)fPrintRect.left; - int top = (int)fPrintRect.top; - int width = fPrintRect.IntegerWidth() + 1; - int height = fPrintRect.IntegerHeight() + 1; + int left = fPrintRect.left; + int top = fPrintRect.top; + int width = fPrintRect.Width() + 1; + int height = fPrintRect.Height() + 1; - fprintf(stderr, "GPJob image dimensions left %d, top %d, width %d, height %d\n", + fprintf(stderr, "GPJob image dimensions left %d, top %d, width %d, " + "height %d\n", left, top, width, height); } - fprintf(stderr, "GPJob image dimensions in 1/72 Inches:\n" - "left %d, top %d, width %d, height %d\n", - left, top, width, height); + fprintf(stderr, "GPJob image dimensions in 1/72 Inches: " + "left %d, top %d, right %d, bottom %d\n", + (int)left, (int)top, (int)right, (int)bottom); - stp_set_width(fVariables, width); - stp_set_height(fVariables, height); + stp_set_width(fVariables, right - left); + stp_set_height(fVariables, bottom - top); stp_set_left(fVariables, left); stp_set_top(fVariables, top); stp_merge_printvars(fVariables, stp_printer_get_defaults(fPrinter)); if (!stp_verify(fVariables)) { - // TODO report error fprintf(stderr, "GPJob PrintPage: invalid variables\n"); return B_ERROR; } @@ -315,7 +396,7 @@ GPJob::GetErrorMessage(BString& message) } -BRect +RectInt32 GPJob::GetPrintRectangle(list& bands) { list::iterator it = bands.begin(); @@ -349,14 +430,14 @@ GPJob::Reset() int GPJob::Width() { - return static_cast(fPrintRect.IntegerWidth() + 1); + return fPrintRect.Width() + 1; } int GPJob::Height() { - return static_cast(fPrintRect.IntegerHeight() + 1); + return fPrintRect.Height() + 1; } @@ -367,8 +448,8 @@ GPJob::GetRow(unsigned char* data, size_t size, int row) return STP_IMAGE_STATUS_ABORT; // row is relative to left, top of image - // convert it to absolute value - int line = static_cast(fPrintRect.top) + row; + // convert it to absolute y coordinate value + int line = fPrintRect.top + row; FillWhite(data, size); @@ -407,19 +488,30 @@ GPJob::FillRow(GPBand* band, unsigned char* data, size_t size, int line) band->fValidRect.top); int imageLeft = static_cast(band->fValidRect.left); - const int sourceDelta = band->fBitmap.BytesPerRow(); + const int sourceBytesPerRow = band->fBitmap.BytesPerRow(); const int kSourceBytesPerPixel = 4; // BGRA const unsigned char* source = static_cast(band->fBitmap.Bits()) - + imageTop * sourceDelta + + imageTop * sourceBytesPerRow + imageLeft * kSourceBytesPerPixel; int dataLeft = static_cast(band->fWhere.x - fPrintRect.left); + int sourcePixelsToSkip = 0; + if (dataLeft < 0) { + sourcePixelsToSkip = -dataLeft; + dataLeft = 0; + } + int width = band->fValidRect.IntegerWidth() + 1 - sourcePixelsToSkip; + source += sourcePixelsToSkip * kSourceBytesPerPixel; + if (width <= 0) + return; const int kTargetBytesPerPixel = 3; // RGB unsigned char* target = &data[dataLeft * kTargetBytesPerPixel]; + int maxWidth = size / kTargetBytesPerPixel - dataLeft; + if (width > maxWidth) + width = maxWidth; - const int width = band->fValidRect.IntegerWidth() + 1; ASSERT(0 <= imageTop && imageTop <= band->fValidRect.IntegerHeight()); ASSERT((dataLeft + width) * kTargetBytesPerPixel <= size); diff --git a/src/add-ons/print/drivers/gutenprint/GPJob.h b/src/add-ons/print/drivers/gutenprint/GPJob.h index 14d89ae502..80a7454f9e 100644 --- a/src/add-ons/print/drivers/gutenprint/GPJob.h +++ b/src/add-ons/print/drivers/gutenprint/GPJob.h @@ -17,6 +17,7 @@ #include "GPBand.h" #include "GPJobConfiguration.h" #include "OutputStream.h" +#include "Rectangle.h" class GPJob @@ -37,11 +38,11 @@ public: void GetErrorMessage(BString& message); private: - BRect GetPrintRectangle(list& bands); - GPBand* FindBand(int line); - void FillRow(GPBand* band, unsigned char* data, size_t size, + RectInt32 GetPrintRectangle(list& bands); + GPBand* FindBand(int line); + void FillRow(GPBand* band, unsigned char* data, size_t size, int line); - void FillWhite(unsigned char* data, size_t size); + void FillWhite(unsigned char* data, size_t size); void Init(); void Reset(); @@ -74,7 +75,7 @@ private: stp_image_t fImage; stp_vars_t* fVariables; const stp_printer_t* fPrinter; - BRect fPrintRect; + RectInt32 fPrintRect; list* fBands; GPBand* fCachedBand; status_t fStatus; diff --git a/src/add-ons/print/drivers/gutenprint/Rectangle.h b/src/add-ons/print/drivers/gutenprint/Rectangle.h new file mode 100644 index 0000000000..fff6976131 --- /dev/null +++ b/src/add-ons/print/drivers/gutenprint/Rectangle.h @@ -0,0 +1,79 @@ +/* +* Copyright 2010, Haiku. All rights reserved. +* Distributed under the terms of the MIT License. +* +* Authors: +* Michael Pfeiffer +*/ +#ifndef RECTANGLE_H +#define RECTANGLE_H + + +#include + + +template +class Rectangle +{ +public: + Rectangle() + : + left(0), + top(0), + right(0), + bottom(0) + { + + } + + + Rectangle(const BRect& rect) + : + left(static_cast(rect.left)), + top(static_cast(rect.top)), + right(static_cast(rect.right)), + bottom(static_cast(rect.bottom)) + { + } + + + Rectangle(T left, T top, T right, T bottom) + : + left(left), + top(top), + right(right), + bottom(bottom) + { + } + + + Rectangle& operator=(const BRect& rect) { + left = static_cast(rect.left); + top = static_cast(rect.top); + right = static_cast(rect.right); + bottom = static_cast(rect.bottom); + return *this; + } + + + T Width() const { + return right - left; + } + + + T Height() const { + return bottom - top; + } + + + T left; + T top; + T right; + T bottom; +}; + + +typedef Rectangle RectInt32; + + +#endif