blob: 51bf8aeccd759ef867a0fd7bde8ad3f12682ff4a [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"
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000025#include "SkRect.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000026#include "SkStream.h"
27#include "SkString.h"
28#include "SkUnPreMultiply.h"
29
30namespace {
31
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000032SkMemoryStream* extractImageData(const SkBitmap& bitmap,
33 const SkIRect& srcRect) {
reed@google.com07700442010-12-20 19:46:07 +000034 SkMemoryStream* result = NULL;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000035
vandebo@chromium.orgad114952010-10-26 19:43:14 +000036 bitmap.lockPixels();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000037 switch (bitmap.getConfig()) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000038 case SkBitmap::kIndex8_Config: {
39 const int rowBytes = srcRect.width();
40 result = new SkMemoryStream(rowBytes * srcRect.height());
41 uint8_t* dst = (uint8_t*)result->getMemoryBase();
42 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
43 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
44 dst += rowBytes;
45 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000046 break;
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000047 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000048 case SkBitmap::kRLE_Index8_Config: {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000049 const int rowBytes = srcRect.width();
50 result = new SkMemoryStream(rowBytes * srcRect.height());
51 uint8_t* dst = (uint8_t*)result->getMemoryBase();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000052 const SkBitmap::RLEPixels* rle =
53 (const SkBitmap::RLEPixels*)bitmap.getPixels();
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000054 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
55 SkPackBits::Unpack8(dst, srcRect.fLeft, rowBytes,
56 rle->packedAtY(y));
57 dst += rowBytes;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000058 }
59 break;
60 }
61 case SkBitmap::kARGB_4444_Config: {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000062 const int rowBytes = (srcRect.width() * 3 + 1) / 2;
63 result = new SkMemoryStream(rowBytes * srcRect.height());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000064 uint8_t* dst = (uint8_t*)result->getMemoryBase();
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000065 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000066 uint16_t* src = bitmap.getAddr16(0, y);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000067 int x;
68 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
69 dst[0] = (SkGetPackedR4444(src[x]) << 4) |
70 SkGetPackedG4444(src[x]);
71 dst[1] = (SkGetPackedB4444(src[x]) << 4) |
72 SkGetPackedR4444(src[x + 1]);
73 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
74 SkGetPackedB4444(src[x + 1]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000075 dst += 3;
76 }
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000077 if (srcRect.width() & 1) {
78 dst[0] = (SkGetPackedR4444(src[x]) << 4) |
79 SkGetPackedG4444(src[x]);
80 dst[1] = (SkGetPackedB4444(src[x]) << 4);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000081 }
82 }
83 break;
84 }
85 case SkBitmap::kRGB_565_Config: {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000086 const int rowBytes = srcRect.width() * 3;
87 result = new SkMemoryStream(rowBytes * srcRect.height());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000088 uint8_t* dst = (uint8_t*)result->getMemoryBase();
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000089 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000090 uint16_t* src = bitmap.getAddr16(0, y);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000091 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
92 dst[0] = SkGetPackedR16(src[x]);
93 dst[1] = SkGetPackedG16(src[x]);
94 dst[2] = SkGetPackedB16(src[x]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000095 dst += 3;
96 }
97 }
98 break;
99 }
100 case SkBitmap::kARGB_8888_Config: {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000101 const int rowBytes = srcRect.width() * 3;
102 result = new SkMemoryStream(rowBytes * srcRect.height());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000103 uint8_t* dst = (uint8_t*)result->getMemoryBase();
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000104 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000105 uint32_t* src = bitmap.getAddr32(0, y);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000106 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
107 dst[0] = SkGetPackedR32(src[x]);
108 dst[1] = SkGetPackedG32(src[x]);
109 dst[2] = SkGetPackedB32(src[x]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000110 dst += 3;
111 }
112 }
113 break;
114 }
115 default:
116 SkASSERT(false);
117 }
vandebo@chromium.orgad114952010-10-26 19:43:14 +0000118 bitmap.unlockPixels();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000119 return result;
120}
121
122SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
123 SkPDFArray* result = new SkPDFArray();
124 result->reserve(4);
125 SkRefPtr<SkPDFName> indexedName = new SkPDFName("Indexed");
126 indexedName->unref(); // SkRefPtr and new both took a reference.
127 result->append(indexedName.get());
128
129 SkRefPtr<SkPDFName> rgbName = new SkPDFName("DeviceRGB");
130 rgbName->unref(); // SkRefPtr and new both took a reference.
131 result->append(rgbName.get());
132
133 rgbName->unref(); // SkRefPtr and new both took a reference.
134 SkRefPtr<SkPDFInt> countValue = new SkPDFInt(table->count() - 1);
135 result->append(countValue.get());
136
137 // Potentially, this could be represented in fewer bytes with a stream.
138 // Max size as a string is 1.5k.
139 SkString index;
140 for (int i = 0; i < table->count(); i++) {
141 char buf[3];
142 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
143 buf[0] = SkGetPackedR32(color);
144 buf[1] = SkGetPackedG32(color);
145 buf[2] = SkGetPackedB32(color);
146 index.append(buf, 3);
147 }
148 SkRefPtr<SkPDFString> indexValue = new SkPDFString(index);
149 indexValue->unref(); // SkRefPtr and new both took a reference.
150 result->append(indexValue.get());
151 return result;
152}
153
154}; // namespace
155
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000156SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkIRect& srcRect,
157 const SkPaint& paint) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000158 SkBitmap::Config config = bitmap.getConfig();
159
160 // TODO(vandebo) Handle alpha and alpha only images correctly.
161 SkASSERT(config == SkBitmap::kRGB_565_Config ||
162 config == SkBitmap::kARGB_4444_Config ||
163 config == SkBitmap::kARGB_8888_Config ||
164 config == SkBitmap::kIndex8_Config ||
165 config == SkBitmap::kRLE_Index8_Config);
166
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000167 SkMemoryStream* image_data = extractImageData(bitmap, srcRect);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000168 SkAutoUnref image_data_unref(image_data);
169 fStream = new SkPDFStream(image_data);
170 fStream->unref(); // SkRefPtr and new both took a reference.
171
172 SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject");
173 typeValue->unref(); // SkRefPtr and new both took a reference.
174 insert("Type", typeValue.get());
175
176 SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image");
177 subTypeValue->unref(); // SkRefPtr and new both took a reference.
178 insert("Subtype", subTypeValue.get());
179
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000180 SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(srcRect.width());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000181 widthValue->unref(); // SkRefPtr and new both took a reference.
182 insert("Width", widthValue.get());
183
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000184 SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(srcRect.height());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000185 heightValue->unref(); // SkRefPtr and new both took a reference.
186 insert("Height", heightValue.get());
187
188 // if (!image mask) {
189 SkRefPtr<SkPDFObject> colorSpaceValue;
190 if (config == SkBitmap::kIndex8_Config ||
191 config == SkBitmap::kRLE_Index8_Config) {
192 colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable());
193 } else {
194 colorSpaceValue = new SkPDFName("DeviceRGB");
195 }
196 colorSpaceValue->unref(); // SkRefPtr and new both took a reference.
197 insert("ColorSpace", colorSpaceValue.get());
198 // }
199
200 int bitsPerComp = bitmap.bytesPerPixel() * 2;
201 if (bitsPerComp == 0) {
202 SkASSERT(config == SkBitmap::kA1_Config);
203 bitsPerComp = 1;
204 } else if (bitsPerComp == 2 ||
205 (bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) {
206 bitsPerComp = 8;
207 }
208 SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp);
209 bitsPerCompValue->unref(); // SkRefPtr and new both took a reference.
210 insert("BitsPerComponent", bitsPerCompValue.get());
211
212 if (config == SkBitmap::kRGB_565_Config) {
213 SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
214 zeroVal->unref(); // SkRefPtr and new both took a reference.
215 SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1
216 scale5Val->unref(); // SkRefPtr and new both took a reference.
217 SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1
218 scale6Val->unref(); // SkRefPtr and new both took a reference.
219 SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
220 decodeValue->unref(); // SkRefPtr and new both took a reference.
221 decodeValue->reserve(6);
222 decodeValue->append(zeroVal.get());
223 decodeValue->append(scale5Val.get());
224 decodeValue->append(zeroVal.get());
225 decodeValue->append(scale6Val.get());
226 decodeValue->append(zeroVal.get());
227 decodeValue->append(scale5Val.get());
228 insert("Decode", decodeValue.get());
229 }
230}
231
232SkPDFImage::~SkPDFImage() {}
233
234void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
235 bool indirect) {
236 if (indirect)
237 return emitIndirectObject(stream, catalog);
238
239 fStream->emitObject(stream, catalog, indirect);
240}
241
242size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
243 if (indirect)
244 return getIndirectOutputSize(catalog);
245
246 return fStream->getOutputSize(catalog, indirect);
247}
248
249void SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
250 fStream->insert(key, value);
251}
252
253void SkPDFImage::insert(const char key[], SkPDFObject* value) {
254 fStream->insert(key, value);
255}