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