namespace {
const uint32 kBitsPerColor = 8;
-const uint32 kColorSpace = 19; // sRGB.
const uint32 kColorOrder = 0; // chunky.
-const uint32 kNumColors = 3;
-const uint32 kBitsPerPixel = kNumColors * kBitsPerColor;
-const char kPwgKeyword[] = "RaS2";
+// Coefficients used to convert from RGB to monochrome.
+const uint32 kRedCoefficient = 2125;
+const uint32 kGreenCoefficient = 7154;
+const uint32 kBlueCoefficient = 0721;
+const uint32 kColorCoefficientDenominator = 10000;
+
+const char* kPwgKeyword = "RaS2";
const uint32 kHeaderSize = 1796;
-const uint32 kHeaderHwResolutionHorizontal = 276;
-const uint32 kHeaderHwResolutionVertical = 280;
+const uint32 kHeaderCupsDuplex = 272;
+const uint32 kHeaderCupsHwResolutionHorizontal = 276;
+const uint32 kHeaderCupsHwResolutionVertical = 280;
+const uint32 kHeaderCupsTumble = 368;
const uint32 kHeaderCupsWidth = 372;
const uint32 kHeaderCupsHeight = 376;
const uint32 kHeaderCupsBitsPerColor = 384;
const uint32 kHeaderCupsColorSpace = 400;
const uint32 kHeaderCupsNumColors = 420;
const uint32 kHeaderPwgTotalPageCount = 452;
+const uint32 kHeaderPwgCrossFeedTransform = 456;
+const uint32 kHeaderPwgFeedTransform = 460;
const int kPwgMaxPackedRows = 256;
const int kPwgMaxPackedPixels = 128;
-inline int FlipIfNeeded(bool flip, int current, int total) {
- return flip ? total - current : current;
-}
-
-} // namespace
+struct RGBA8 {
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ uint8 alpha;
+};
-PwgEncoder::PwgEncoder() {}
+struct BGRA8 {
+ uint8 blue;
+ uint8 green;
+ uint8 red;
+ uint8 alpha;
+};
-inline void encodePixelFromRGBA(const uint8* pixel, std::string* output) {
- output->push_back(static_cast<char>(pixel[0]));
- output->push_back(static_cast<char>(pixel[1]));
- output->push_back(static_cast<char>(pixel[2]));
+template <class InputStruct>
+inline void encodePixelToRGB(const void* pixel, std::string* output) {
+ const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel);
+ output->push_back(static_cast<char>(i->red));
+ output->push_back(static_cast<char>(i->green));
+ output->push_back(static_cast<char>(i->blue));
}
-inline void encodePixelFromBGRA(const uint8* pixel, std::string* output) {
- output->push_back(static_cast<char>(pixel[2]));
- output->push_back(static_cast<char>(pixel[1]));
- output->push_back(static_cast<char>(pixel[0]));
+template <class InputStruct>
+inline void encodePixelToMonochrome(const void* pixel, std::string* output) {
+ const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel);
+ output->push_back(static_cast<char>((i->red * kRedCoefficient +
+ i->green * kGreenCoefficient +
+ i->blue * kBlueCoefficient) /
+ kColorCoefficientDenominator));
}
+} // namespace
+
+PwgEncoder::PwgEncoder() {}
+
void PwgEncoder::EncodeDocumentHeader(std::string* output) const {
output->clear();
output->append(kPwgKeyword, 4);
}
-void PwgEncoder::EncodePageHeader(const BitmapImage& image, const uint32 dpi,
- const uint32 total_pages,
+void PwgEncoder::EncodePageHeader(const BitmapImage& image,
+ const PwgHeaderInfo& pwg_header_info,
std::string* output) const {
char header[kHeaderSize];
memset(header, 0, kHeaderSize);
- base::WriteBigEndian<uint32>(header + kHeaderHwResolutionHorizontal, dpi);
- base::WriteBigEndian<uint32>(header + kHeaderHwResolutionVertical, dpi);
+
+ uint32 num_colors =
+ pwg_header_info.color_space == PwgHeaderInfo::SGRAY ? 1 : 3;
+ uint32 bits_per_pixel = num_colors * kBitsPerColor;
+
+ base::WriteBigEndian<uint32>(header + kHeaderCupsDuplex,
+ pwg_header_info.duplex ? 1 : 0);
+ base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionHorizontal,
+ pwg_header_info.dpi);
+ base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionVertical,
+ pwg_header_info.dpi);
+ base::WriteBigEndian<uint32>(header + kHeaderCupsTumble,
+ pwg_header_info.tumble ? 1 : 0);
base::WriteBigEndian<uint32>(header + kHeaderCupsWidth, image.size().width());
base::WriteBigEndian<uint32>(header + kHeaderCupsHeight,
image.size().height());
base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerColor, kBitsPerColor);
- base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel, kBitsPerPixel);
+ base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel,
+ bits_per_pixel);
base::WriteBigEndian<uint32>(header + kHeaderCupsBytesPerLine,
- (kBitsPerPixel * image.size().width() + 7) / 8);
+ (bits_per_pixel * image.size().width() + 7) / 8);
base::WriteBigEndian<uint32>(header + kHeaderCupsColorOrder, kColorOrder);
- base::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace, kColorSpace);
- base::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, kNumColors);
- base::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount, total_pages);
+ base::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace,
+ pwg_header_info.color_space);
+ base::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, num_colors);
+ base::WriteBigEndian<uint32>(header + kHeaderPwgCrossFeedTransform,
+ pwg_header_info.flipx ? -1 : 1);
+ base::WriteBigEndian<uint32>(header + kHeaderPwgFeedTransform,
+ pwg_header_info.flipy ? -1 : 1);
+ base::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount,
+ pwg_header_info.total_pages);
output->append(header, kHeaderSize);
}
-bool PwgEncoder::EncodeRowFrom32Bit(const uint8* row, const int width,
- const int color_space,
- std::string* output) const {
- void (*pixel_encoder)(const uint8*, std::string*);
- switch (color_space) {
- case BitmapImage::RGBA:
- pixel_encoder = &encodePixelFromRGBA;
- break;
- case BitmapImage::BGRA:
- pixel_encoder = &encodePixelFromBGRA;
- break;
- default:
- LOG(ERROR) << "Unsupported colorspace.";
- return false;
- }
-
- // Converts the list of uint8 to uint32 as every pixels contains 4 bytes
- // of information and comparison of elements is easier. The actual management
- // of the bytes of the pixel is done by template function P on the original
- // array to avoid endian problems.
- const uint32* pos = reinterpret_cast<const uint32*>(row);
- const uint32* row_end = pos + width;
+template <typename InputStruct, class RandomAccessIterator>
+void PwgEncoder::EncodeRow(RandomAccessIterator pos,
+ RandomAccessIterator row_end,
+ bool monochrome,
+ std::string* output) const {
// According to PWG-raster, a sequence of N identical pixels (up to 128)
// can be encoded by a byte N-1, followed by the information on
- // that pixel. Any generic sequence of N pixels (up to 128) can be encoded
+ // that pixel. Any generic sequence of N pixels (up to 129) can be encoded
// with (signed) byte 1-N, followed by the information on the N pixels.
// Notice that for sequences of 1 pixel there is no difference between
// the two encodings.
- // It is usually better to encode every largest sequence of > 2 identical
- // pixels together because it saves the most space. Every other pixel should
- // be encoded in the smallest number of generic sequences.
+ // We encode every largest sequence of identical pixels together because it
+ // usually saves the most space. Every other pixel should be encoded in the
+ // smallest number of generic sequences.
+ // NOTE: the algorithm is not optimal especially in case of monochrome.
while (pos != row_end) {
- const uint32* it = pos + 1;
- const uint32* end = std::min(pos + kPwgMaxPackedPixels, row_end);
+ RandomAccessIterator it = pos + 1;
+ RandomAccessIterator end = std::min(pos + kPwgMaxPackedPixels, row_end);
// Counts how many identical pixels (up to 128).
while (it != end && *pos == *it) {
- it++;
+ ++it;
}
if (it != pos + 1) { // More than one pixel
output->push_back(static_cast<char>((it - pos) - 1));
- pixel_encoder(reinterpret_cast<const uint8*>(pos), output);
+ if (monochrome)
+ encodePixelToMonochrome<InputStruct>(&*pos, output);
+ else
+ encodePixelToRGB<InputStruct>(&*pos, output);
pos = it;
} else {
- // Finds how many pixels each different from the previous one (up to 128).
+ // Finds how many pixels there are each different from the previous one.
+ // IMPORTANT: even if sequences of different pixels can contain as many
+ // as 129 pixels, we restrict to 128 because some decoders don't manage
+ // it correctly. So iterating until it != end is correct.
while (it != end && *it != *(it - 1)) {
- it++;
+ ++it;
}
// Optimization: ignores the last pixel of the sequence if it is followed
// by an identical pixel, as it is more convenient for it to be the start
// of a new sequence of identical pixels. Notice that we don't compare
// to end, but row_end.
if (it != row_end && *it == *(it - 1)) {
- it--;
+ --it;
}
output->push_back(static_cast<char>(1 - (it - pos)));
while (pos != it) {
- pixel_encoder(reinterpret_cast<const uint8*>(pos++), output);
+ if (monochrome)
+ encodePixelToMonochrome<InputStruct>(&*pos, output);
+ else
+ encodePixelToRGB<InputStruct>(&*pos, output);
+ ++pos;
}
}
}
- return true;
}
inline const uint8* PwgEncoder::GetRow(const BitmapImage& image,
- int row) const {
- return image.pixel_data() + row * image.size().width() * image.channels();
+ int row,
+ bool flipy) const {
+ return image.GetPixel(
+ gfx::Point(0, flipy ? image.size().height() - 1 - row : row));
}
// Given a pointer to a struct Image, create a PWG of the image and
-// put the compressed image data in the std::string. Returns true on success.
-// The content of the std::string is undefined on failure.
+// put the compressed image data in the string. Returns true on success.
+// The content of the string is undefined on failure.
bool PwgEncoder::EncodePage(const BitmapImage& image,
- const uint32 dpi,
- const uint32 total_pages,
- std::string* output,
- bool rotate) const {
- // For now only some 4-channel colorspaces are supported.
- if (image.channels() != 4) {
- LOG(ERROR) << "Unsupported colorspace.";
- return false;
+ const PwgHeaderInfo& pwg_header_info,
+ std::string* output) const {
+ // pwg_header_info.color_space can only contain color spaces that are
+ // supported, so no sanity check is needed.
+ switch (image.colorspace()) {
+ case BitmapImage::RGBA:
+ return EncodePageWithColorspace<RGBA8>(image, pwg_header_info, output);
+
+ case BitmapImage::BGRA:
+ return EncodePageWithColorspace<BGRA8>(image, pwg_header_info, output);
+
+ default:
+ LOG(ERROR) << "Unsupported colorspace.";
+ return false;
}
+}
- EncodePageHeader(image, dpi, total_pages, output);
+template <typename InputStruct>
+bool PwgEncoder::EncodePageWithColorspace(const BitmapImage& image,
+ const PwgHeaderInfo& pwg_header_info,
+ std::string* output) const {
+ bool monochrome = pwg_header_info.color_space == PwgHeaderInfo::SGRAY;
+ EncodePageHeader(image, pwg_header_info, output);
+ // Ensure no integer overflow.
+ CHECK(image.size().width() < INT_MAX / image.channels());
int row_size = image.size().width() * image.channels();
- scoped_ptr<uint8[]> current_row_cpy(new uint8[row_size]);
int row_number = 0;
- int total_rows = image.size().height();
- while (row_number < total_rows) {
+ while (row_number < image.size().height()) {
const uint8* current_row =
- GetRow(image, FlipIfNeeded(rotate, row_number++, total_rows));
+ GetRow(image, row_number++, pwg_header_info.flipy);
int num_identical_rows = 1;
// We count how many times the current row is repeated.
while (num_identical_rows < kPwgMaxPackedRows &&
row_number < image.size().height() &&
!memcmp(current_row,
- GetRow(image, FlipIfNeeded(rotate, row_number, total_rows)),
+ GetRow(image, row_number, pwg_header_info.flipy),
row_size)) {
num_identical_rows++;
row_number++;
}
output->push_back(static_cast<char>(num_identical_rows - 1));
- if (rotate) {
- memcpy(current_row_cpy.get(), current_row, row_size);
- std::reverse(reinterpret_cast<uint32*>(current_row_cpy.get()),
- reinterpret_cast<uint32*>(current_row_cpy.get() + row_size));
- current_row = current_row_cpy.get();
- }
-
// Both supported colorspaces have a 32-bit pixels information.
- if (!EncodeRowFrom32Bit(
- current_row, image.size().width(), image.colorspace(), output)) {
- return false;
+ // Converts the list of uint8 to uint32 as every pixels contains 4 bytes
+ // of information and comparison of elements is easier. The actual
+ // Management of the bytes of the pixel is done by pixel_encoder function
+ // on the original array to avoid endian problems.
+ const uint32* pos = reinterpret_cast<const uint32*>(current_row);
+ const uint32* row_end = pos + image.size().width();
+ if (!pwg_header_info.flipx) {
+ EncodeRow<InputStruct>(pos, row_end, monochrome, output);
+ } else {
+ // We reverse the iterators.
+ EncodeRow<InputStruct>(std::reverse_iterator<const uint32*>(row_end),
+ std::reverse_iterator<const uint32*>(pos),
+ monochrome,
+ output);
}
}
return true;