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 kColorSpace
= 19; // sRGB.
20 const uint32 kColorOrder
= 0; // chunky.
21 const uint32 kNumColors
= 3;
22 const uint32 kBitsPerPixel
= kNumColors
* kBitsPerColor
;
24 const char kPwgKeyword
[] = "RaS2";
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;
39 const int kPwgMaxPackedRows
= 256;
41 const int kPwgMaxPackedPixels
= 128;
43 inline int FlipIfNeeded(bool flip
, int current
, int total
) {
44 return flip
? total
- current
: current
;
49 PwgEncoder::PwgEncoder() {}
51 inline void encodePixelFromRGBA(const uint8
* pixel
, std::string
* output
) {
52 output
->push_back(static_cast<char>(pixel
[0]));
53 output
->push_back(static_cast<char>(pixel
[1]));
54 output
->push_back(static_cast<char>(pixel
[2]));
57 inline void encodePixelFromBGRA(const uint8
* pixel
, std::string
* output
) {
58 output
->push_back(static_cast<char>(pixel
[2]));
59 output
->push_back(static_cast<char>(pixel
[1]));
60 output
->push_back(static_cast<char>(pixel
[0]));
63 void PwgEncoder::EncodeDocumentHeader(std::string
* output
) const {
65 output
->append(kPwgKeyword
, 4);
68 void PwgEncoder::EncodePageHeader(const BitmapImage
& image
, const uint32 dpi
,
69 const uint32 total_pages
,
70 std::string
* output
) const {
71 char header
[kHeaderSize
];
72 memset(header
, 0, kHeaderSize
);
73 base::WriteBigEndian
<uint32
>(header
+ kHeaderHwResolutionHorizontal
, dpi
);
74 base::WriteBigEndian
<uint32
>(header
+ kHeaderHwResolutionVertical
, dpi
);
75 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsWidth
, image
.size().width());
76 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsHeight
,
77 image
.size().height());
78 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsBitsPerColor
, kBitsPerColor
);
79 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsBitsPerPixel
, kBitsPerPixel
);
80 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsBytesPerLine
,
81 (kBitsPerPixel
* image
.size().width() + 7) / 8);
82 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsColorOrder
, kColorOrder
);
83 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsColorSpace
, kColorSpace
);
84 base::WriteBigEndian
<uint32
>(header
+ kHeaderCupsNumColors
, kNumColors
);
85 base::WriteBigEndian
<uint32
>(header
+ kHeaderPwgTotalPageCount
, total_pages
);
86 output
->append(header
, kHeaderSize
);
89 bool PwgEncoder::EncodeRowFrom32Bit(const uint8
* row
, const int width
,
90 const int color_space
,
91 std::string
* output
) const {
92 void (*pixel_encoder
)(const uint8
*, std::string
*);
93 switch (color_space
) {
94 case BitmapImage::RGBA
:
95 pixel_encoder
= &encodePixelFromRGBA
;
97 case BitmapImage::BGRA
:
98 pixel_encoder
= &encodePixelFromBGRA
;
101 LOG(ERROR
) << "Unsupported colorspace.";
105 // Converts the list of uint8 to uint32 as every pixels contains 4 bytes
106 // of information and comparison of elements is easier. The actual management
107 // of the bytes of the pixel is done by template function P on the original
108 // array to avoid endian problems.
109 const uint32
* pos
= reinterpret_cast<const uint32
*>(row
);
110 const uint32
* row_end
= pos
+ width
;
111 // According to PWG-raster, a sequence of N identical pixels (up to 128)
112 // can be encoded by a byte N-1, followed by the information on
113 // that pixel. Any generic sequence of N pixels (up to 128) can be encoded
114 // with (signed) byte 1-N, followed by the information on the N pixels.
115 // Notice that for sequences of 1 pixel there is no difference between
116 // the two encodings.
118 // It is usually better to encode every largest sequence of > 2 identical
119 // pixels together because it saves the most space. Every other pixel should
120 // be encoded in the smallest number of generic sequences.
121 while (pos
!= row_end
) {
122 const uint32
* it
= pos
+ 1;
123 const uint32
* end
= std::min(pos
+ kPwgMaxPackedPixels
, row_end
);
125 // Counts how many identical pixels (up to 128).
126 while (it
!= end
&& *pos
== *it
) {
129 if (it
!= pos
+ 1) { // More than one pixel
130 output
->push_back(static_cast<char>((it
- pos
) - 1));
131 pixel_encoder(reinterpret_cast<const uint8
*>(pos
), output
);
134 // Finds how many pixels each different from the previous one (up to 128).
135 while (it
!= end
&& *it
!= *(it
- 1)) {
138 // Optimization: ignores the last pixel of the sequence if it is followed
139 // by an identical pixel, as it is more convenient for it to be the start
140 // of a new sequence of identical pixels. Notice that we don't compare
141 // to end, but row_end.
142 if (it
!= row_end
&& *it
== *(it
- 1)) {
145 output
->push_back(static_cast<char>(1 - (it
- pos
)));
147 pixel_encoder(reinterpret_cast<const uint8
*>(pos
++), output
);
154 inline const uint8
* PwgEncoder::GetRow(const BitmapImage
& image
,
156 return image
.pixel_data() + row
* image
.size().width() * image
.channels();
159 // Given a pointer to a struct Image, create a PWG of the image and
160 // put the compressed image data in the std::string. Returns true on success.
161 // The content of the std::string is undefined on failure.
162 bool PwgEncoder::EncodePage(const BitmapImage
& image
,
164 const uint32 total_pages
,
167 // For now only some 4-channel colorspaces are supported.
168 if (image
.channels() != 4) {
169 LOG(ERROR
) << "Unsupported colorspace.";
173 EncodePageHeader(image
, dpi
, total_pages
, output
);
175 int row_size
= image
.size().width() * image
.channels();
176 scoped_ptr
<uint8
[]> current_row_cpy(new uint8
[row_size
]);
179 int total_rows
= image
.size().height();
180 while (row_number
< total_rows
) {
181 const uint8
* current_row
=
182 GetRow(image
, FlipIfNeeded(rotate
, row_number
++, total_rows
));
183 int num_identical_rows
= 1;
184 // We count how many times the current row is repeated.
185 while (num_identical_rows
< kPwgMaxPackedRows
&&
186 row_number
< image
.size().height() &&
188 GetRow(image
, FlipIfNeeded(rotate
, row_number
, total_rows
)),
190 num_identical_rows
++;
193 output
->push_back(static_cast<char>(num_identical_rows
- 1));
196 memcpy(current_row_cpy
.get(), current_row
, row_size
);
197 std::reverse(reinterpret_cast<uint32
*>(current_row_cpy
.get()),
198 reinterpret_cast<uint32
*>(current_row_cpy
.get() + row_size
));
199 current_row
= current_row_cpy
.get();
202 // Both supported colorspaces have a 32-bit pixels information.
203 if (!EncodeRowFrom32Bit(
204 current_row
, image
.size().width(), image
.colorspace(), output
)) {
211 } // namespace cloud_print