• 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/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "chrome/utility/cloud_print/bitmap_image.h"
12 #include "net/base/big_endian.h"
13 
14 namespace cloud_print {
15 
16 namespace {
17 
18 const uint32 kBitsPerColor = 8;
19 const uint32 kColorSpace = 19;  // sRGB.
20 const uint32 kColorOrder = 0;  // chunky.
21 const uint32 kNumColors = 3;
22 const uint32 kBitsPerPixel = kNumColors * kBitsPerColor;
23 
24 const char kPwgKeyword[] = "RaS2";
25 
26 const uint32 kHeaderSize = 1796;
27 const uint32 kHeaderHwResolutionHorizontal = 276;
28 const uint32 kHeaderHwResolutionVertical = 280;
29 const uint32 kHeaderCupsWidth = 372;
30 const uint32 kHeaderCupsHeight = 376;
31 const uint32 kHeaderCupsBitsPerColor = 384;
32 const uint32 kHeaderCupsBitsPerPixel = 388;
33 const uint32 kHeaderCupsBytesPerLine = 392;
34 const uint32 kHeaderCupsColorOrder = 396;
35 const uint32 kHeaderCupsColorSpace = 400;
36 const uint32 kHeaderCupsNumColors = 420;
37 const uint32 kHeaderPwgTotalPageCount = 452;
38 
39 const int kPwgMaxPackedRows = 256;
40 
41 const int kPwgMaxPackedPixels = 128;
42 
43 }  // namespace
44 
PwgEncoder()45 PwgEncoder::PwgEncoder() {}
46 
encodePixelFromRGBA(const uint8 * pixel,std::string * output)47 inline void encodePixelFromRGBA(const uint8* pixel, std::string* output) {
48   output->push_back(static_cast<char>(pixel[0]));
49   output->push_back(static_cast<char>(pixel[1]));
50   output->push_back(static_cast<char>(pixel[2]));
51 }
52 
encodePixelFromBGRA(const uint8 * pixel,std::string * output)53 inline void encodePixelFromBGRA(const uint8* pixel, std::string* output) {
54   output->push_back(static_cast<char>(pixel[2]));
55   output->push_back(static_cast<char>(pixel[1]));
56   output->push_back(static_cast<char>(pixel[0]));
57 }
58 
EncodeDocumentHeader(std::string * output) const59 void PwgEncoder::EncodeDocumentHeader(std::string* output) const {
60   output->clear();
61   output->append(kPwgKeyword, 4);
62 }
63 
EncodePageHeader(const BitmapImage & image,const uint32 dpi,const uint32 total_pages,std::string * output) const64 void PwgEncoder::EncodePageHeader(const BitmapImage& image, const uint32 dpi,
65                                   const uint32 total_pages,
66                                   std::string* output) const {
67   char header[kHeaderSize];
68   memset(header, 0, kHeaderSize);
69   net::WriteBigEndian<uint32>(header + kHeaderHwResolutionHorizontal, dpi);
70   net::WriteBigEndian<uint32>(header + kHeaderHwResolutionVertical, dpi);
71   net::WriteBigEndian<uint32>(header + kHeaderCupsWidth, image.size().width());
72   net::WriteBigEndian<uint32>(header + kHeaderCupsHeight,
73                               image.size().height());
74   net::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerColor, kBitsPerColor);
75   net::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel, kBitsPerPixel);
76   net::WriteBigEndian<uint32>(header + kHeaderCupsBytesPerLine,
77                               (kBitsPerPixel * image.size().width() + 7) / 8);
78   net::WriteBigEndian<uint32>(header + kHeaderCupsColorOrder, kColorOrder);
79   net::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace, kColorSpace);
80   net::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, kNumColors);
81   net::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount, total_pages);
82   output->append(header, kHeaderSize);
83 }
84 
EncodeRowFrom32Bit(const uint8 * row,const int width,const int color_space,std::string * output) const85 bool PwgEncoder::EncodeRowFrom32Bit(const uint8* row, const int width,
86                                     const int color_space,
87                                     std::string* output) const {
88   void (*pixel_encoder)(const uint8*, std::string*);
89   switch (color_space) {
90     case BitmapImage::RGBA:
91       pixel_encoder = &encodePixelFromRGBA;
92       break;
93     case BitmapImage::BGRA:
94       pixel_encoder = &encodePixelFromBGRA;
95       break;
96     default:
97       LOG(ERROR) << "Unsupported colorspace.";
98       return false;
99   }
100 
101   // Converts the list of uint8 to uint32 as every pixels contains 4 bytes
102   // of information and comparison of elements is easier. The actual management
103   // of the bytes of the pixel is done by template function P on the original
104   // array to avoid endian problems.
105   const uint32* pos = reinterpret_cast<const uint32*>(row);
106   const uint32* row_end = pos + width;
107   // According to PWG-raster, a sequence of N identical pixels (up to 128)
108   // can be encoded by a byte N-1, followed by the information on
109   // that pixel. Any generic sequence of N pixels (up to 128) can be encoded
110   // with (signed) byte 1-N, followed by the information on the N pixels.
111   // Notice that for sequences of 1 pixel there is no difference between
112   // the two encodings.
113 
114   // It is usually better to encode every largest sequence of > 2 identical
115   // pixels together because it saves the most space. Every other pixel should
116   // be encoded in the smallest number of generic sequences.
117   while (pos != row_end) {
118     const uint32* it = pos + 1;
119     const uint32* end = std::min(pos + kPwgMaxPackedPixels, row_end);
120 
121     // Counts how many identical pixels (up to 128).
122     while (it != end && *pos == *it) {
123       it++;
124     }
125     if (it != pos + 1) {  // More than one pixel
126       output->push_back(static_cast<char>((it - pos) - 1));
127       pixel_encoder(reinterpret_cast<const uint8*>(pos), output);
128       pos = it;
129     } else {
130       // Finds how many pixels each different from the previous one (up to 128).
131       while (it != end && *it != *(it - 1)) {
132         it++;
133       }
134       // Optimization: ignores the last pixel of the sequence if it is followed
135       // by an identical pixel, as it is more convenient for it to be the start
136       // of a new sequence of identical pixels. Notice that we don't compare
137       // to end, but row_end.
138       if (it != row_end && *it == *(it - 1)) {
139         it--;
140       }
141       output->push_back(static_cast<char>(1 - (it - pos)));
142       while (pos != it) {
143         pixel_encoder(reinterpret_cast<const uint8*>(pos++), output);
144       }
145     }
146   }
147   return true;
148 }
149 
GetRow(const BitmapImage & image,int row) const150 inline const uint8* PwgEncoder::GetRow(const BitmapImage& image,
151                                        int row) const {
152   return image.pixel_data() + row * image.size().width() * image.channels();
153 }
154 
155 // Given a pointer to a struct Image, create a PWG of the image and
156 // put the compressed image data in the std::string.  Returns true on success.
157 // The content of the std::string is undefined on failure.
EncodePage(const BitmapImage & image,const uint32 dpi,const uint32 total_pages,std::string * output) const158 bool PwgEncoder::EncodePage(const BitmapImage& image,
159                             const uint32 dpi,
160                             const uint32 total_pages,
161                             std::string* output) const {
162   // For now only some 4-channel colorspaces are supported.
163   if (image.channels() != 4) {
164     LOG(ERROR) << "Unsupported colorspace.";
165     return false;
166   }
167 
168   EncodePageHeader(image, dpi, total_pages, output);
169 
170   int row_size = image.size().width() * image.channels();
171 
172   int row_number = 0;
173   while (row_number < image.size().height()) {
174     const uint8* current_row = GetRow(image, row_number++);
175     int num_identical_rows = 1;
176     // We count how many times the current row is repeated.
177     while (num_identical_rows < kPwgMaxPackedRows
178            && row_number < image.size().height()
179            && !memcmp(current_row, GetRow(image, row_number), row_size)) {
180       num_identical_rows++;
181       row_number++;
182     }
183     output->push_back(static_cast<char>(num_identical_rows - 1));
184 
185     // Both supported colorspaces have a 32-bit pixels information.
186     if (!EncodeRowFrom32Bit(
187             current_row, image.size().width(), image.colorspace(), output)) {
188       return false;
189     }
190   }
191   return true;
192 }
193 
194 }  // namespace cloud_print
195