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.
5 #include "chrome/utility/cloud_print/pwg_encoder.h"
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"
14 namespace cloud_print
{
18 const uint32 kBitsPerColor
= 8;
19 const uint32 kColorOrder
= 0; // chunky.
21 // Coefficients used to convert from RGB to monochrome.
22 const uint32 kRedCoefficient
= 2125;
23 const uint32 kGreenCoefficient
= 7154;
24 const uint32 kBlueCoefficient
= 721;
25 const uint32 kColorCoefficientDenominator
= 10000;
27 const char kPwgKeyword
[] = "RaS2";
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;
46 const int kPwgMaxPackedRows
= 256;
48 const int kPwgMaxPackedPixels
= 128;
64 template <class InputStruct
>
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
));
72 template <class InputStruct
>
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
));
83 PwgEncoder::PwgEncoder() {}
85 void PwgEncoder::EncodeDocumentHeader(std::string
* output
) const {
87 output
->append(kPwgKeyword
, 4);
90 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
);
97 pwg_header_info
.color_space
== PwgHeaderInfo::SGRAY
? 1 : 3;
98 uint32 bits_per_pixel
= num_colors
* kBitsPerColor
;
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
,
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
);
129 template <typename InputStruct
, class RandomAccessIterator
>
130 void PwgEncoder::EncodeRow(RandomAccessIterator pos
,
131 RandomAccessIterator row_end
,
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.
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
);
149 // Counts how many identical pixels (up to 128).
150 while (it
!= end
&& *pos
== *it
) {
153 if (it
!= pos
+ 1) { // More than one pixel
154 output
->push_back(static_cast<char>((it
- pos
) - 1));
156 encodePixelToMonochrome
<InputStruct
>(&*pos
, output
);
158 encodePixelToRGB
<InputStruct
>(&*pos
, output
);
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)) {
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)) {
175 output
->push_back(static_cast<char>(1 - (it
- pos
)));
178 encodePixelToMonochrome
<InputStruct
>(&*pos
, output
);
180 encodePixelToRGB
<InputStruct
>(&*pos
, output
);
187 inline const uint8
* PwgEncoder::GetRow(const BitmapImage
& image
,
190 return image
.GetPixel(
191 gfx::Point(0, flipy
? image
.size().height() - 1 - row
: row
));
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.
197 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
);
206 case BitmapImage::BGRA
:
207 return EncodePageWithColorspace
<BGRA8
>(image
, pwg_header_info
, output
);
210 LOG(ERROR
) << "Unsupported colorspace.";
215 template <typename InputStruct
>
216 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
);
222 // Ensure no integer overflow.
223 CHECK(image
.size().width() < INT_MAX
/ image
.channels());
224 int row_size
= image
.size().width() * image
.channels();
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() &&
235 GetRow(image
, row_number
, pwg_header_info
.flipy
),
237 num_identical_rows
++;
240 output
->push_back(static_cast<char>(num_identical_rows
- 1));
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
);
252 // We reverse the iterators.
253 EncodeRow
<InputStruct
>(std::reverse_iterator
<const uint32
*>(row_end
),
254 std::reverse_iterator
<const uint32
*>(pos
),
262 } // namespace cloud_print