blob: 65b9b245140a82182aaeb9b37b766a0ad6107e36 [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
vandebo@chromium.orgad114952010-10-26 19:43:14 +000034 bitmap.lockPixels();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000035 switch (bitmap.getConfig()) {
36 case SkBitmap::kIndex8_Config:
37 result = new SkMemoryStream(bitmap.getPixels(), bitmap.getSize(),
38 true);
39 break;
40 case SkBitmap::kRLE_Index8_Config: {
41 result = new SkMemoryStream(bitmap.getSize());
42 const SkBitmap::RLEPixels* rle =
43 (const SkBitmap::RLEPixels*)bitmap.getPixels();
44 uint8_t* dst = (uint8_t*)result->getMemoryBase();
45 const int width = bitmap.width();
46 for (int y = 0; y < bitmap.height(); y++) {
47 SkPackBits::Unpack8(rle->packedAtY(y), width, dst);
48 dst += width;
49 }
50 break;
51 }
52 case SkBitmap::kARGB_4444_Config: {
53 const int width = bitmap.width();
54 const int rowBytes = (width * 3 + 1) / 2;
55 result = new SkMemoryStream(rowBytes * bitmap.height());
56 uint8_t* dst = (uint8_t*)result->getMemoryBase();
57 for (int y = 0; y < bitmap.height(); y++) {
58 uint16_t* src = bitmap.getAddr16(0, y);
59 for (int x = 0; x < width; x += 2) {
60 dst[0] = (SkGetPackedR4444(src[0]) << 4) |
61 SkGetPackedG4444(src[0]);
62 dst[1] = (SkGetPackedB4444(src[0]) << 4) |
63 SkGetPackedR4444(src[1]);
64 dst[2] = (SkGetPackedG4444(src[1]) << 4) |
65 SkGetPackedB4444(src[1]);
66 src += 2;
67 dst += 3;
68 }
69 if (width & 1) {
70 dst[0] = (SkGetPackedR4444(src[0]) << 4) |
71 SkGetPackedG4444(src[0]);
72 dst[1] = (SkGetPackedB4444(src[0]) << 4);
73 }
74 }
75 break;
76 }
77 case SkBitmap::kRGB_565_Config: {
78 const int width = bitmap.width();
79 const int rowBytes = width * 3;
80 result = new SkMemoryStream(rowBytes * bitmap.height());
81 uint8_t* dst = (uint8_t*)result->getMemoryBase();
82 for (int y = 0; y < bitmap.height(); y++) {
83 uint16_t* src = bitmap.getAddr16(0, y);
84 for (int x = 0; x < width; x++) {
85 dst[0] = SkGetPackedR16(src[0]);
86 dst[1] = SkGetPackedG16(src[0]);
87 dst[2] = SkGetPackedB16(src[0]);
88 src++;
89 dst += 3;
90 }
91 }
92 break;
93 }
94 case SkBitmap::kARGB_8888_Config: {
95 const int width = bitmap.width();
96 const int rowBytes = width * 3;
97 result = new SkMemoryStream(rowBytes * bitmap.height());
98 uint8_t* dst = (uint8_t*)result->getMemoryBase();
99 for (int y = 0; y < bitmap.height(); y++) {
100 uint32_t* src = bitmap.getAddr32(0, y);
101 for (int x = 0; x < width; x++) {
102 dst[0] = SkGetPackedR32(src[0]);
103 dst[1] = SkGetPackedG32(src[0]);
104 dst[2] = SkGetPackedB32(src[0]);
105 src++;
106 dst += 3;
107 }
108 }
109 break;
110 }
111 default:
112 SkASSERT(false);
113 }
vandebo@chromium.orgad114952010-10-26 19:43:14 +0000114 bitmap.unlockPixels();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000115 return result;
116}
117
118SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
119 SkPDFArray* result = new SkPDFArray();
120 result->reserve(4);
121 SkRefPtr<SkPDFName> indexedName = new SkPDFName("Indexed");
122 indexedName->unref(); // SkRefPtr and new both took a reference.
123 result->append(indexedName.get());
124
125 SkRefPtr<SkPDFName> rgbName = new SkPDFName("DeviceRGB");
126 rgbName->unref(); // SkRefPtr and new both took a reference.
127 result->append(rgbName.get());
128
129 rgbName->unref(); // SkRefPtr and new both took a reference.
130 SkRefPtr<SkPDFInt> countValue = new SkPDFInt(table->count() - 1);
131 result->append(countValue.get());
132
133 // Potentially, this could be represented in fewer bytes with a stream.
134 // Max size as a string is 1.5k.
135 SkString index;
136 for (int i = 0; i < table->count(); i++) {
137 char buf[3];
138 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
139 buf[0] = SkGetPackedR32(color);
140 buf[1] = SkGetPackedG32(color);
141 buf[2] = SkGetPackedB32(color);
142 index.append(buf, 3);
143 }
144 SkRefPtr<SkPDFString> indexValue = new SkPDFString(index);
145 indexValue->unref(); // SkRefPtr and new both took a reference.
146 result->append(indexValue.get());
147 return result;
148}
149
150}; // namespace
151
152SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint) {
153 SkBitmap::Config config = bitmap.getConfig();
154
155 // TODO(vandebo) Handle alpha and alpha only images correctly.
156 SkASSERT(config == SkBitmap::kRGB_565_Config ||
157 config == SkBitmap::kARGB_4444_Config ||
158 config == SkBitmap::kARGB_8888_Config ||
159 config == SkBitmap::kIndex8_Config ||
160 config == SkBitmap::kRLE_Index8_Config);
161
162 SkMemoryStream* image_data = extractImageData(bitmap);
163 SkAutoUnref image_data_unref(image_data);
164 fStream = new SkPDFStream(image_data);
165 fStream->unref(); // SkRefPtr and new both took a reference.
166
167 SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject");
168 typeValue->unref(); // SkRefPtr and new both took a reference.
169 insert("Type", typeValue.get());
170
171 SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image");
172 subTypeValue->unref(); // SkRefPtr and new both took a reference.
173 insert("Subtype", subTypeValue.get());
174
175 SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(bitmap.width());
176 widthValue->unref(); // SkRefPtr and new both took a reference.
177 insert("Width", widthValue.get());
178
179 SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(bitmap.height());
180 heightValue->unref(); // SkRefPtr and new both took a reference.
181 insert("Height", heightValue.get());
182
183 // if (!image mask) {
184 SkRefPtr<SkPDFObject> colorSpaceValue;
185 if (config == SkBitmap::kIndex8_Config ||
186 config == SkBitmap::kRLE_Index8_Config) {
187 colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable());
188 } else {
189 colorSpaceValue = new SkPDFName("DeviceRGB");
190 }
191 colorSpaceValue->unref(); // SkRefPtr and new both took a reference.
192 insert("ColorSpace", colorSpaceValue.get());
193 // }
194
195 int bitsPerComp = bitmap.bytesPerPixel() * 2;
196 if (bitsPerComp == 0) {
197 SkASSERT(config == SkBitmap::kA1_Config);
198 bitsPerComp = 1;
199 } else if (bitsPerComp == 2 ||
200 (bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) {
201 bitsPerComp = 8;
202 }
203 SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp);
204 bitsPerCompValue->unref(); // SkRefPtr and new both took a reference.
205 insert("BitsPerComponent", bitsPerCompValue.get());
206
207 if (config == SkBitmap::kRGB_565_Config) {
208 SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
209 zeroVal->unref(); // SkRefPtr and new both took a reference.
210 SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1
211 scale5Val->unref(); // SkRefPtr and new both took a reference.
212 SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1
213 scale6Val->unref(); // SkRefPtr and new both took a reference.
214 SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
215 decodeValue->unref(); // SkRefPtr and new both took a reference.
216 decodeValue->reserve(6);
217 decodeValue->append(zeroVal.get());
218 decodeValue->append(scale5Val.get());
219 decodeValue->append(zeroVal.get());
220 decodeValue->append(scale6Val.get());
221 decodeValue->append(zeroVal.get());
222 decodeValue->append(scale5Val.get());
223 insert("Decode", decodeValue.get());
224 }
225}
226
227SkPDFImage::~SkPDFImage() {}
228
229void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
230 bool indirect) {
231 if (indirect)
232 return emitIndirectObject(stream, catalog);
233
234 fStream->emitObject(stream, catalog, indirect);
235}
236
237size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
238 if (indirect)
239 return getIndirectOutputSize(catalog);
240
241 return fStream->getOutputSize(catalog, indirect);
242}
243
244void SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
245 fStream->insert(key, value);
246}
247
248void SkPDFImage::insert(const char key[], SkPDFObject* value) {
249 fStream->insert(key, value);
250}