• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
14 namespace cloud_print {
15 
16 namespace {
17 
18 const uint32 kBitsPerColor = 8;
19 const uint32 kColorOrder = 0;  // chunky.
20 
21 // Coefficients used to convert from RGB to monochrome.
22 const uint32 kRedCoefficient = 2125;
23 const uint32 kGreenCoefficient = 7154;
24 const uint32 kBlueCoefficient = 0721;
25 const uint32 kColorCoefficientDenominator = 10000;
26 
27 const char* kPwgKeyword = "RaS2";
28 
29 const uint32 kHeaderSize = 1796;
30 const uint32 kHeaderCupsDuplex = 272;
31 const uint32 kHeaderCupsHwResolutionHorizontal = 276;
32 const uint32 kHeaderCupsHwResolutionVertical = 280;
33 const uint32 kHeaderCupsTumble = 368;
34 const uint32 kHeaderCupsWidth = 372;
35 const uint32 kHeaderCupsHeight = 376;
36 const uint32 kHeaderCupsBitsPerColor = 384;
37 const uint32 kHeaderCupsBitsPerPixel = 388;
38 const uint32 kHeaderCupsBytesPerLine = 392;
39 const uint32 kHeaderCupsColorOrder = 396;
40 const uint32 kHeaderCupsColorSpace = 400;
41 const uint32 kHeaderCupsNumColors = 420;
42 const uint32 kHeaderPwgTotalPageCount = 452;
43 const uint32 kHeaderPwgCrossFeedTransform = 456;
44 const uint32 kHeaderPwgFeedTransform = 460;
45 
46 const int kPwgMaxPackedRows = 256;
47 
48 const int kPwgMaxPackedPixels = 128;
49 
50 struct RGBA8 {
51   uint8 red;
52   uint8 green;
53   uint8 blue;
54   uint8 alpha;
55 };
56 
57 struct BGRA8 {
58   uint8 blue;
59   uint8 green;
60   uint8 red;
61   uint8 alpha;
62 };
63 
64 template <class InputStruct>
encodePixelToRGB(const void * pixel,std::string * output)65 inline 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 
72 template <class InputStruct>
encodePixelToMonochrome(const void * pixel,std::string * output)73 inline 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 
PwgEncoder()83 PwgEncoder::PwgEncoder() {}
84 
EncodeDocumentHeader(std::string * output) const85 void PwgEncoder::EncodeDocumentHeader(std::string* output) const {
86   output->clear();
87   output->append(kPwgKeyword, 4);
88 }
89 
EncodePageHeader(const BitmapImage & image,const PwgHeaderInfo & pwg_header_info,std::string * output) const90 void 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 
129 template <typename InputStruct, class RandomAccessIterator>
EncodeRow(RandomAccessIterator pos,RandomAccessIterator row_end,bool monochrome,std::string * output) const130 void 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 
GetRow(const BitmapImage & image,int row,bool flipy) const187 inline 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.
EncodePage(const BitmapImage & image,const PwgHeaderInfo & pwg_header_info,std::string * output) const197 bool 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 
215 template <typename InputStruct>
EncodePageWithColorspace(const BitmapImage & image,const PwgHeaderInfo & pwg_header_info,std::string * output) const216 bool 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