123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/pwg_encoder/pwg_encoder.h"
- #include <limits.h>
- #include <string.h>
- #include <algorithm>
- #include <memory>
- #include "base/big_endian.h"
- #include "base/logging.h"
- #include "components/pwg_encoder/bitmap_image.h"
- namespace pwg_encoder {
- namespace {
- const uint32_t kBitsPerColor = 8;
- const uint32_t kColorOrder = 0; // chunky.
- // Coefficients used to convert from RGB to monochrome.
- const uint32_t kRedCoefficient = 2125;
- const uint32_t kGreenCoefficient = 7154;
- const uint32_t kBlueCoefficient = 721;
- const uint32_t kColorCoefficientDenominator = 10000;
- const char kPwgKeyword[] = "RaS2";
- const uint32_t kHeaderSize = 1796;
- const uint32_t kHeaderCupsDuplex = 272;
- const uint32_t kHeaderCupsHwResolutionHorizontal = 276;
- const uint32_t kHeaderCupsHwResolutionVertical = 280;
- const uint32_t kHeaderCupsTumble = 368;
- const uint32_t kHeaderCupsWidth = 372;
- const uint32_t kHeaderCupsHeight = 376;
- const uint32_t kHeaderCupsBitsPerColor = 384;
- const uint32_t kHeaderCupsBitsPerPixel = 388;
- const uint32_t kHeaderCupsBytesPerLine = 392;
- const uint32_t kHeaderCupsColorOrder = 396;
- const uint32_t kHeaderCupsColorSpace = 400;
- const uint32_t kHeaderCupsNumColors = 420;
- const uint32_t kHeaderPwgTotalPageCount = 452;
- const uint32_t kHeaderPwgCrossFeedTransform = 456;
- const uint32_t kHeaderPwgFeedTransform = 460;
- const int kPwgMaxPackedRows = 256;
- const int kPwgMaxPackedPixels = 128;
- struct RGBA8 {
- uint8_t red;
- uint8_t green;
- uint8_t blue;
- uint8_t alpha;
- };
- struct BGRA8 {
- uint8_t blue;
- uint8_t green;
- uint8_t red;
- uint8_t alpha;
- };
- template <class InputStruct>
- 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));
- }
- template <class InputStruct>
- 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));
- }
- std::string EncodePageHeader(const BitmapImage& image,
- const PwgHeaderInfo& pwg_header_info) {
- char header[kHeaderSize];
- memset(header, 0, kHeaderSize);
- uint32_t num_colors =
- pwg_header_info.color_space == PwgHeaderInfo::SGRAY ? 1 : 3;
- uint32_t bits_per_pixel = num_colors * kBitsPerColor;
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsDuplex,
- pwg_header_info.duplex ? 1 : 0);
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsHwResolutionHorizontal,
- pwg_header_info.dpi.width());
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsHwResolutionVertical,
- pwg_header_info.dpi.height());
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsTumble,
- pwg_header_info.tumble ? 1 : 0);
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsWidth,
- image.size().width());
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsHeight,
- image.size().height());
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsBitsPerColor,
- kBitsPerColor);
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsBitsPerPixel,
- bits_per_pixel);
- base::WriteBigEndian<uint32_t>(
- header + kHeaderCupsBytesPerLine,
- (bits_per_pixel * image.size().width() + 7) / 8);
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsColorOrder, kColorOrder);
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsColorSpace,
- pwg_header_info.color_space);
- base::WriteBigEndian<uint32_t>(header + kHeaderCupsNumColors, num_colors);
- base::WriteBigEndian<uint32_t>(header + kHeaderPwgCrossFeedTransform,
- pwg_header_info.flipx ? -1 : 1);
- base::WriteBigEndian<uint32_t>(header + kHeaderPwgFeedTransform,
- pwg_header_info.flipy ? -1 : 1);
- base::WriteBigEndian<uint32_t>(header + kHeaderPwgTotalPageCount,
- pwg_header_info.total_pages);
- return std::string(header, kHeaderSize);
- }
- template <typename InputStruct, class RandomAccessIterator>
- void EncodeRow(RandomAccessIterator pos,
- RandomAccessIterator row_end,
- bool monochrome,
- std::string* output) {
- // 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 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.
- // 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) {
- 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;
- }
- if (it != pos + 1) { // More than one pixel
- output->push_back(static_cast<char>((it - pos) - 1));
- if (monochrome)
- EncodePixelToMonochrome<InputStruct>(&*pos, output);
- else
- EncodePixelToRGB<InputStruct>(&*pos, output);
- pos = it;
- } else {
- // 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;
- }
- // 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;
- }
- output->push_back(static_cast<char>(1 - (it - pos)));
- while (pos != it) {
- if (monochrome)
- EncodePixelToMonochrome<InputStruct>(&*pos, output);
- else
- EncodePixelToRGB<InputStruct>(&*pos, output);
- ++pos;
- }
- }
- }
- }
- const uint8_t* GetRow(const BitmapImage& image, int row, bool flipy) {
- return image.GetPixel(
- gfx::Point(0, flipy ? image.size().height() - 1 - row : row));
- }
- template <typename InputStruct>
- std::string EncodePageWithColorspace(const BitmapImage& image,
- const PwgHeaderInfo& pwg_header_info) {
- bool monochrome = pwg_header_info.color_space == PwgHeaderInfo::SGRAY;
- std::string output = EncodePageHeader(image, pwg_header_info);
- // Ensure no integer overflow.
- CHECK(image.size().width() < INT_MAX / image.channels());
- int row_size = image.size().width() * image.channels();
- int row_number = 0;
- while (row_number < image.size().height()) {
- const uint8_t* current_row =
- 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, row_number, pwg_header_info.flipy),
- row_size)) {
- num_identical_rows++;
- row_number++;
- }
- output.push_back(static_cast<char>(num_identical_rows - 1));
- // Both supported colorspaces have a 32-bit pixels information.
- // Converts the list of uint8_t to uint32_t 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_t* pos = reinterpret_cast<const uint32_t*>(current_row);
- const uint32_t* 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_t*>(row_end),
- std::reverse_iterator<const uint32_t*>(pos),
- monochrome, &output);
- }
- }
- return output;
- }
- } // namespace
- // static
- std::string PwgEncoder::GetDocumentHeader() {
- std::string output;
- output.append(kPwgKeyword, 4);
- return output;
- }
- // static
- std::string PwgEncoder::EncodePage(const BitmapImage& image,
- const PwgHeaderInfo& pwg_header_info) {
- // pwg_header_info.color_space can only contain color spaces that are
- // supported, so no sanity check is needed.
- std::string data;
- switch (image.colorspace()) {
- case BitmapImage::RGBA:
- data = EncodePageWithColorspace<RGBA8>(image, pwg_header_info);
- break;
- case BitmapImage::BGRA:
- data = EncodePageWithColorspace<BGRA8>(image, pwg_header_info);
- break;
- default:
- LOG(ERROR) << "Unsupported colorspace.";
- break;
- }
- return data;
- }
- } // namespace pwg_encoder
|