1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/utility/cloud_print/pwg_encoder.h"
6
7#include <algorithm>
8
9#include "base/big_endian.h"
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "chrome/utility/cloud_print/bitmap_image.h"
13
14namespace cloud_print {
15
16namespace {
17
18const uint32 kBitsPerColor = 8;
19const uint32 kColorOrder = 0;  // chunky.
20
21// Coefficients used to convert from RGB to monochrome.
22const uint32 kRedCoefficient = 2125;
23const uint32 kGreenCoefficient = 7154;
24const uint32 kBlueCoefficient = 721;
25const uint32 kColorCoefficientDenominator = 10000;
26
27const char* kPwgKeyword = "RaS2";
28
29const uint32 kHeaderSize = 1796;
30const uint32 kHeaderCupsDuplex = 272;
31const uint32 kHeaderCupsHwResolutionHorizontal = 276;
32const uint32 kHeaderCupsHwResolutionVertical = 280;
33const uint32 kHeaderCupsTumble = 368;
34const uint32 kHeaderCupsWidth = 372;
35const uint32 kHeaderCupsHeight = 376;
36const uint32 kHeaderCupsBitsPerColor = 384;
37const uint32 kHeaderCupsBitsPerPixel = 388;
38const uint32 kHeaderCupsBytesPerLine = 392;
39const uint32 kHeaderCupsColorOrder = 396;
40const uint32 kHeaderCupsColorSpace = 400;
41const uint32 kHeaderCupsNumColors = 420;
42const uint32 kHeaderPwgTotalPageCount = 452;
43const uint32 kHeaderPwgCrossFeedTransform = 456;
44const uint32 kHeaderPwgFeedTransform = 460;
45
46const int kPwgMaxPackedRows = 256;
47
48const int kPwgMaxPackedPixels = 128;
49
50struct RGBA8 {
51  uint8 red;
52  uint8 green;
53  uint8 blue;
54  uint8 alpha;
55};
56
57struct BGRA8 {
58  uint8 blue;
59  uint8 green;
60  uint8 red;
61  uint8 alpha;
62};
63
64template <class InputStruct>
65inline void encodePixelToRGB(const void* pixel, std::string* output) {
66  const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel);
67  output->push_back(static_cast<char>(i->red));
68  output->push_back(static_cast<char>(i->green));
69  output->push_back(static_cast<char>(i->blue));
70}
71
72template <class InputStruct>
73inline void encodePixelToMonochrome(const void* pixel, std::string* output) {
74  const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel);
75  output->push_back(static_cast<char>((i->red * kRedCoefficient +
76                                       i->green * kGreenCoefficient +
77                                       i->blue * kBlueCoefficient) /
78                                      kColorCoefficientDenominator));
79}
80
81}  // namespace
82
83PwgEncoder::PwgEncoder() {}
84
85void PwgEncoder::EncodeDocumentHeader(std::string* output) const {
86  output->clear();
87  output->append(kPwgKeyword, 4);
88}
89
90void PwgEncoder::EncodePageHeader(const BitmapImage& image,
91                                  const PwgHeaderInfo& pwg_header_info,
92                                  std::string* output) const {
93  char header[kHeaderSize];
94  memset(header, 0, kHeaderSize);
95
96  uint32 num_colors =
97      pwg_header_info.color_space == PwgHeaderInfo::SGRAY ? 1 : 3;
98  uint32 bits_per_pixel = num_colors * kBitsPerColor;
99
100  base::WriteBigEndian<uint32>(header + kHeaderCupsDuplex,
101                               pwg_header_info.duplex ? 1 : 0);
102  base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionHorizontal,
103                               pwg_header_info.dpi);
104  base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionVertical,
105                               pwg_header_info.dpi);
106  base::WriteBigEndian<uint32>(header + kHeaderCupsTumble,
107                               pwg_header_info.tumble ? 1 : 0);
108  base::WriteBigEndian<uint32>(header + kHeaderCupsWidth, image.size().width());
109  base::WriteBigEndian<uint32>(header + kHeaderCupsHeight,
110                               image.size().height());
111  base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerColor, kBitsPerColor);
112  base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel,
113                               bits_per_pixel);
114  base::WriteBigEndian<uint32>(header + kHeaderCupsBytesPerLine,
115                               (bits_per_pixel * image.size().width() + 7) / 8);
116  base::WriteBigEndian<uint32>(header + kHeaderCupsColorOrder, kColorOrder);
117  base::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace,
118                               pwg_header_info.color_space);
119  base::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, num_colors);
120  base::WriteBigEndian<uint32>(header + kHeaderPwgCrossFeedTransform,
121                               pwg_header_info.flipx ? -1 : 1);
122  base::WriteBigEndian<uint32>(header + kHeaderPwgFeedTransform,
123                               pwg_header_info.flipy ? -1 : 1);
124  base::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount,
125                               pwg_header_info.total_pages);
126  output->append(header, kHeaderSize);
127}
128
129template <typename InputStruct, class RandomAccessIterator>
130void PwgEncoder::EncodeRow(RandomAccessIterator pos,
131                           RandomAccessIterator row_end,
132                           bool monochrome,
133                           std::string* output) const {
134  // According to PWG-raster, a sequence of N identical pixels (up to 128)
135  // can be encoded by a byte N-1, followed by the information on
136  // that pixel. Any generic sequence of N pixels (up to 129) can be encoded
137  // with (signed) byte 1-N, followed by the information on the N pixels.
138  // Notice that for sequences of 1 pixel there is no difference between
139  // the two encodings.
140
141  // We encode every largest sequence of identical pixels together because it
142  // usually saves the most space. Every other pixel should be encoded in the
143  // smallest number of generic sequences.
144  // NOTE: the algorithm is not optimal especially in case of monochrome.
145  while (pos != row_end) {
146    RandomAccessIterator it = pos + 1;
147    RandomAccessIterator end = std::min(pos + kPwgMaxPackedPixels, row_end);
148
149    // Counts how many identical pixels (up to 128).
150    while (it != end && *pos == *it) {
151      ++it;
152    }
153    if (it != pos + 1) {  // More than one pixel
154      output->push_back(static_cast<char>((it - pos) - 1));
155      if (monochrome)
156        encodePixelToMonochrome<InputStruct>(&*pos, output);
157      else
158        encodePixelToRGB<InputStruct>(&*pos, output);
159      pos = it;
160    } else {
161      // Finds how many pixels there are each different from the previous one.
162      // IMPORTANT: even if sequences of different pixels can contain as many
163      // as 129 pixels, we restrict to 128 because some decoders don't manage
164      // it correctly. So iterating until it != end is correct.
165      while (it != end && *it != *(it - 1)) {
166        ++it;
167      }
168      // Optimization: ignores the last pixel of the sequence if it is followed
169      // by an identical pixel, as it is more convenient for it to be the start
170      // of a new sequence of identical pixels. Notice that we don't compare
171      // to end, but row_end.
172      if (it != row_end && *it == *(it - 1)) {
173        --it;
174      }
175      output->push_back(static_cast<char>(1 - (it - pos)));
176      while (pos != it) {
177        if (monochrome)
178          encodePixelToMonochrome<InputStruct>(&*pos, output);
179        else
180          encodePixelToRGB<InputStruct>(&*pos, output);
181        ++pos;
182      }
183    }
184  }
185}
186
187inline const uint8* PwgEncoder::GetRow(const BitmapImage& image,
188                                       int row,
189                                       bool flipy) const {
190  return image.GetPixel(
191      gfx::Point(0, flipy ? image.size().height() - 1 - row : row));
192}
193
194// Given a pointer to a struct Image, create a PWG of the image and
195// put the compressed image data in the string.  Returns true on success.
196// The content of the string is undefined on failure.
197bool PwgEncoder::EncodePage(const BitmapImage& image,
198                            const PwgHeaderInfo& pwg_header_info,
199                            std::string* output) const {
200  // pwg_header_info.color_space can only contain color spaces that are
201  // supported, so no sanity check is needed.
202  switch (image.colorspace()) {
203    case BitmapImage::RGBA:
204      return EncodePageWithColorspace<RGBA8>(image, pwg_header_info, output);
205
206    case BitmapImage::BGRA:
207      return EncodePageWithColorspace<BGRA8>(image, pwg_header_info, output);
208
209    default:
210      LOG(ERROR) << "Unsupported colorspace.";
211      return false;
212  }
213}
214
215template <typename InputStruct>
216bool PwgEncoder::EncodePageWithColorspace(const BitmapImage& image,
217                                          const PwgHeaderInfo& pwg_header_info,
218                                          std::string* output) const {
219  bool monochrome = pwg_header_info.color_space == PwgHeaderInfo::SGRAY;
220  EncodePageHeader(image, pwg_header_info, output);
221
222  // Ensure no integer overflow.
223  CHECK(image.size().width() < INT_MAX / image.channels());
224  int row_size = image.size().width() * image.channels();
225
226  int row_number = 0;
227  while (row_number < image.size().height()) {
228    const uint8* current_row =
229        GetRow(image, row_number++, pwg_header_info.flipy);
230    int num_identical_rows = 1;
231    // We count how many times the current row is repeated.
232    while (num_identical_rows < kPwgMaxPackedRows &&
233           row_number < image.size().height() &&
234           !memcmp(current_row,
235                   GetRow(image, row_number, pwg_header_info.flipy),
236                   row_size)) {
237      num_identical_rows++;
238      row_number++;
239    }
240    output->push_back(static_cast<char>(num_identical_rows - 1));
241
242    // Both supported colorspaces have a 32-bit pixels information.
243    // Converts the list of uint8 to uint32 as every pixels contains 4 bytes
244    // of information and comparison of elements is easier. The actual
245    // Management of the bytes of the pixel is done by pixel_encoder function
246    // on the original array to avoid endian problems.
247    const uint32* pos = reinterpret_cast<const uint32*>(current_row);
248    const uint32* row_end = pos + image.size().width();
249    if (!pwg_header_info.flipx) {
250      EncodeRow<InputStruct>(pos, row_end, monochrome, output);
251    } else {
252      // We reverse the iterators.
253      EncodeRow<InputStruct>(std::reverse_iterator<const uint32*>(row_end),
254                             std::reverse_iterator<const uint32*>(pos),
255                             monochrome,
256                             output);
257    }
258  }
259  return true;
260}
261
262}  // namespace cloud_print
263