blob: b480089b17a8f51892c72422563f49e6c2de69ea [file] [log] [blame]
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkPDFImage.h"
18
19#include "SkBitmap.h"
20#include "SkColor.h"
21#include "SkColorPriv.h"
22#include "SkPaint.h"
23#include "SkPackBits.h"
24#include "SkPDFCatalog.h"
25#include "SkStream.h"
26#include "SkString.h"
27#include "SkUnPreMultiply.h"
28
29namespace {
30
31SkMemoryStream* extractImageData(const SkBitmap& bitmap) {
32 SkMemoryStream* result;
33
34 switch (bitmap.getConfig()) {
35 case SkBitmap::kIndex8_Config:
36 result = new SkMemoryStream(bitmap.getPixels(), bitmap.getSize(),
37 true);
38 break;
39 case SkBitmap::kRLE_Index8_Config: {
40 result = new SkMemoryStream(bitmap.getSize());
41 const SkBitmap::RLEPixels* rle =
42 (const SkBitmap::RLEPixels*)bitmap.getPixels();
43 uint8_t* dst = (uint8_t*)result->getMemoryBase();
44 const int width = bitmap.width();
45 for (int y = 0; y < bitmap.height(); y++) {
46 SkPackBits::Unpack8(rle->packedAtY(y), width, dst);
47 dst += width;
48 }
49 break;
50 }
51 case SkBitmap::kARGB_4444_Config: {
52 const int width = bitmap.width();
53 const int rowBytes = (width * 3 + 1) / 2;
54 result = new SkMemoryStream(rowBytes * bitmap.height());
55 uint8_t* dst = (uint8_t*)result->getMemoryBase();
56 for (int y = 0; y < bitmap.height(); y++) {
57 uint16_t* src = bitmap.getAddr16(0, y);
58 for (int x = 0; x < width; x += 2) {
59 dst[0] = (SkGetPackedR4444(src[0]) << 4) |
60 SkGetPackedG4444(src[0]);
61 dst[1] = (SkGetPackedB4444(src[0]) << 4) |
62 SkGetPackedR4444(src[1]);
63 dst[2] = (SkGetPackedG4444(src[1]) << 4) |
64 SkGetPackedB4444(src[1]);
65 src += 2;
66 dst += 3;
67 }
68 if (width & 1) {
69 dst[0] = (SkGetPackedR4444(src[0]) << 4) |
70 SkGetPackedG4444(src[0]);
71 dst[1] = (SkGetPackedB4444(src[0]) << 4);
72 }
73 }
74 break;
75 }
76 case SkBitmap::kRGB_565_Config: {
77 const int width = bitmap.width();
78 const int rowBytes = width * 3;
79 result = new SkMemoryStream(rowBytes * bitmap.height());
80 uint8_t* dst = (uint8_t*)result->getMemoryBase();
81 for (int y = 0; y < bitmap.height(); y++) {
82 uint16_t* src = bitmap.getAddr16(0, y);
83 for (int x = 0; x < width; x++) {
84 dst[0] = SkGetPackedR16(src[0]);
85 dst[1] = SkGetPackedG16(src[0]);
86 dst[2] = SkGetPackedB16(src[0]);
87 src++;
88 dst += 3;
89 }
90 }
91 break;
92 }
93 case SkBitmap::kARGB_8888_Config: {
94 const int width = bitmap.width();
95 const int rowBytes = width * 3;
96 result = new SkMemoryStream(rowBytes * bitmap.height());
97 uint8_t* dst = (uint8_t*)result->getMemoryBase();
98 for (int y = 0; y < bitmap.height(); y++) {
99 uint32_t* src = bitmap.getAddr32(0, y);
100 for (int x = 0; x < width; x++) {
101 dst[0] = SkGetPackedR32(src[0]);
102 dst[1] = SkGetPackedG32(src[0]);
103 dst[2] = SkGetPackedB32(src[0]);
104 src++;
105 dst += 3;
106 }
107 }
108 break;
109 }
110 default:
111 SkASSERT(false);
112 }
113 return result;
114}
115
116SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
117 SkPDFArray* result = new SkPDFArray();
118 result->reserve(4);
119 SkRefPtr<SkPDFName> indexedName = new SkPDFName("Indexed");
120 indexedName->unref(); // SkRefPtr and new both took a reference.
121 result->append(indexedName.get());
122
123 SkRefPtr<SkPDFName> rgbName = new SkPDFName("DeviceRGB");
124 rgbName->unref(); // SkRefPtr and new both took a reference.
125 result->append(rgbName.get());
126
127 rgbName->unref(); // SkRefPtr and new both took a reference.
128 SkRefPtr<SkPDFInt> countValue = new SkPDFInt(table->count() - 1);
129 result->append(countValue.get());
130
131 // Potentially, this could be represented in fewer bytes with a stream.
132 // Max size as a string is 1.5k.
133 SkString index;
134 for (int i = 0; i < table->count(); i++) {
135 char buf[3];
136 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
137 buf[0] = SkGetPackedR32(color);
138 buf[1] = SkGetPackedG32(color);
139 buf[2] = SkGetPackedB32(color);
140 index.append(buf, 3);
141 }
142 SkRefPtr<SkPDFString> indexValue = new SkPDFString(index);
143 indexValue->unref(); // SkRefPtr and new both took a reference.
144 result->append(indexValue.get());
145 return result;
146}
147
148}; // namespace
149
150SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint) {
151 SkBitmap::Config config = bitmap.getConfig();
152
153 // TODO(vandebo) Handle alpha and alpha only images correctly.
154 SkASSERT(config == SkBitmap::kRGB_565_Config ||
155 config == SkBitmap::kARGB_4444_Config ||
156 config == SkBitmap::kARGB_8888_Config ||
157 config == SkBitmap::kIndex8_Config ||
158 config == SkBitmap::kRLE_Index8_Config);
159
160 SkMemoryStream* image_data = extractImageData(bitmap);
161 SkAutoUnref image_data_unref(image_data);
162 fStream = new SkPDFStream(image_data);
163 fStream->unref(); // SkRefPtr and new both took a reference.
164
165 SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject");
166 typeValue->unref(); // SkRefPtr and new both took a reference.
167 insert("Type", typeValue.get());
168
169 SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image");
170 subTypeValue->unref(); // SkRefPtr and new both took a reference.
171 insert("Subtype", subTypeValue.get());
172
173 SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(bitmap.width());
174 widthValue->unref(); // SkRefPtr and new both took a reference.
175 insert("Width", widthValue.get());
176
177 SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(bitmap.height());
178 heightValue->unref(); // SkRefPtr and new both took a reference.
179 insert("Height", heightValue.get());
180
181 // if (!image mask) {
182 SkRefPtr<SkPDFObject> colorSpaceValue;
183 if (config == SkBitmap::kIndex8_Config ||
184 config == SkBitmap::kRLE_Index8_Config) {
185 colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable());
186 } else {
187 colorSpaceValue = new SkPDFName("DeviceRGB");
188 }
189 colorSpaceValue->unref(); // SkRefPtr and new both took a reference.
190 insert("ColorSpace", colorSpaceValue.get());
191 // }
192
193 int bitsPerComp = bitmap.bytesPerPixel() * 2;
194 if (bitsPerComp == 0) {
195 SkASSERT(config == SkBitmap::kA1_Config);
196 bitsPerComp = 1;
197 } else if (bitsPerComp == 2 ||
198 (bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) {
199 bitsPerComp = 8;
200 }
201 SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp);
202 bitsPerCompValue->unref(); // SkRefPtr and new both took a reference.
203 insert("BitsPerComponent", bitsPerCompValue.get());
204
205 if (config == SkBitmap::kRGB_565_Config) {
206 SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
207 zeroVal->unref(); // SkRefPtr and new both took a reference.
208 SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1
209 scale5Val->unref(); // SkRefPtr and new both took a reference.
210 SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1
211 scale6Val->unref(); // SkRefPtr and new both took a reference.
212 SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
213 decodeValue->unref(); // SkRefPtr and new both took a reference.
214 decodeValue->reserve(6);
215 decodeValue->append(zeroVal.get());
216 decodeValue->append(scale5Val.get());
217 decodeValue->append(zeroVal.get());
218 decodeValue->append(scale6Val.get());
219 decodeValue->append(zeroVal.get());
220 decodeValue->append(scale5Val.get());
221 insert("Decode", decodeValue.get());
222 }
223}
224
225SkPDFImage::~SkPDFImage() {}
226
227void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
228 bool indirect) {
229 if (indirect)
230 return emitIndirectObject(stream, catalog);
231
232 fStream->emitObject(stream, catalog, indirect);
233}
234
235size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
236 if (indirect)
237 return getIndirectOutputSize(catalog);
238
239 return fStream->getOutputSize(catalog, indirect);
240}
241
242void SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
243 fStream->insert(key, value);
244}
245
246void SkPDFImage::insert(const char key[], SkPDFObject* value) {
247 fStream->insert(key, value);
248}