1
2/*
3 * Copyright 2010 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkPDFImage.h"
11
12#include "SkBitmap.h"
13#include "SkColor.h"
14#include "SkColorPriv.h"
15#include "SkPaint.h"
16#include "SkPackBits.h"
17#include "SkPDFCatalog.h"
18#include "SkRect.h"
19#include "SkStream.h"
20#include "SkString.h"
21#include "SkUnPreMultiply.h"
22
23namespace {
24
25void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect,
26                      SkStream** imageData, SkStream** alphaData) {
27    SkMemoryStream* image = NULL;
28    SkMemoryStream* alpha = NULL;
29    bool hasAlpha = false;
30    bool isTransparent = false;
31
32    bitmap.lockPixels();
33    switch (bitmap.getConfig()) {
34        case SkBitmap::kIndex8_Config: {
35            const int rowBytes = srcRect.width();
36            image = new SkMemoryStream(rowBytes * srcRect.height());
37            uint8_t* dst = (uint8_t*)image->getMemoryBase();
38            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
39                memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
40                dst += rowBytes;
41            }
42            break;
43        }
44        case SkBitmap::kRLE_Index8_Config: {
45            const int rowBytes = srcRect.width();
46            image = new SkMemoryStream(rowBytes * srcRect.height());
47            uint8_t* dst = (uint8_t*)image->getMemoryBase();
48            const SkBitmap::RLEPixels* rle =
49                (const SkBitmap::RLEPixels*)bitmap.getPixels();
50            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
51                SkPackBits::Unpack8(dst, srcRect.fLeft, rowBytes,
52                                    rle->packedAtY(y));
53                dst += rowBytes;
54            }
55            break;
56        }
57        case SkBitmap::kARGB_4444_Config: {
58            isTransparent = true;
59            const int rowBytes = (srcRect.width() * 3 + 1) / 2;
60            const int alphaRowBytes = (srcRect.width() + 1) / 2;
61            image = new SkMemoryStream(rowBytes * srcRect.height());
62            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
63            uint8_t* dst = (uint8_t*)image->getMemoryBase();
64            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
65            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
66                uint16_t* src = bitmap.getAddr16(0, y);
67                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]);
75                    dst += 3;
76                    alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) |
77                        SkGetPackedA4444(src[x + 1]);
78                    if (alphaDst[0] != 0xFF) {
79                        hasAlpha = true;
80                    }
81                    if (alphaDst[0]) {
82                        isTransparent = false;
83                    }
84                    alphaDst++;
85                }
86                if (srcRect.width() & 1) {
87                    dst[0] = (SkGetPackedR4444(src[x]) << 4) |
88                        SkGetPackedG4444(src[x]);
89                    dst[1] = (SkGetPackedB4444(src[x]) << 4);
90                    dst += 2;
91                    alphaDst[0] = (SkGetPackedA4444(src[x]) << 4);
92                    if (alphaDst[0] != 0xF0) {
93                        hasAlpha = true;
94                    }
95                    if (alphaDst[0] & 0xF0) {
96                        isTransparent = false;
97                    }
98                    alphaDst++;
99                }
100            }
101            break;
102        }
103        case SkBitmap::kRGB_565_Config: {
104            const int rowBytes = srcRect.width() * 3;
105            image = new SkMemoryStream(rowBytes * srcRect.height());
106            uint8_t* dst = (uint8_t*)image->getMemoryBase();
107            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
108                uint16_t* src = bitmap.getAddr16(0, y);
109                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
110                    dst[0] = SkGetPackedR16(src[x]);
111                    dst[1] = SkGetPackedG16(src[x]);
112                    dst[2] = SkGetPackedB16(src[x]);
113                    dst += 3;
114                }
115            }
116            break;
117        }
118        case SkBitmap::kARGB_8888_Config: {
119            isTransparent = true;
120            const int rowBytes = srcRect.width() * 3;
121            image = new SkMemoryStream(rowBytes * srcRect.height());
122            alpha = new SkMemoryStream(srcRect.width() * srcRect.height());
123            uint8_t* dst = (uint8_t*)image->getMemoryBase();
124            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
125            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
126                uint32_t* src = bitmap.getAddr32(0, y);
127                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
128                    dst[0] = SkGetPackedR32(src[x]);
129                    dst[1] = SkGetPackedG32(src[x]);
130                    dst[2] = SkGetPackedB32(src[x]);
131                    dst += 3;
132                    alphaDst[0] = SkGetPackedA32(src[x]);
133                    if (alphaDst[0] != 0xFF) {
134                        hasAlpha = true;
135                    }
136                    if (alphaDst[0]) {
137                        isTransparent = false;
138                    }
139                    alphaDst++;
140                }
141            }
142            break;
143        }
144        case SkBitmap::kA1_Config: {
145            isTransparent = true;
146            image = new SkMemoryStream(1);
147            ((uint8_t*)image->getMemoryBase())[0] = 0;
148
149            const int alphaRowBytes = (srcRect.width() + 7) / 8;
150            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
151            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
152            int offset1 = srcRect.fLeft % 8;
153            int offset2 = 8 - offset1;
154            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
155                uint8_t* src = bitmap.getAddr1(0, y);
156                // This may read up to one byte after src, but the potentially
157                // invalid bits are never used for computation.
158                for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8)  {
159                    if (offset1) {
160                        alphaDst[0] = src[x / 8] << offset1 |
161                            src[x / 8 + 1] >> offset2;
162                    } else {
163                        alphaDst[0] = src[x / 8];
164                    }
165                    if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) {
166                        hasAlpha = true;
167                    }
168                    if (x + 7 < srcRect.fRight && alphaDst[0]) {
169                        isTransparent = false;
170                    }
171                    alphaDst++;
172                }
173                // Calculate the mask of bits we're interested in within the
174                // last byte of alphaDst.
175                // width mod 8  == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE
176                uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1);
177                if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) {
178                    hasAlpha = true;
179                }
180                if (srcRect.width() % 8 && (alphaDst[-1] & mask)) {
181                    isTransparent = false;
182                }
183            }
184            break;
185        }
186        case SkBitmap::kA8_Config: {
187            isTransparent = true;
188            image = new SkMemoryStream(1);
189            ((uint8_t*)image->getMemoryBase())[0] = 0;
190
191            const int alphaRowBytes = srcRect.width();
192            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
193            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
194            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
195                uint8_t* src = bitmap.getAddr8(0, y);
196                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
197                    alphaDst[0] = src[x];
198                    if (alphaDst[0] != 0xFF) {
199                        hasAlpha = true;
200                    }
201                    if (alphaDst[0]) {
202                        isTransparent = false;
203                    }
204                    alphaDst++;
205                }
206            }
207            break;
208        }
209        default:
210            SkASSERT(false);
211    }
212    bitmap.unlockPixels();
213
214    if (isTransparent) {
215        SkSafeUnref(image);
216    } else {
217        *imageData = image;
218    }
219
220    if (isTransparent || !hasAlpha) {
221        SkSafeUnref(alpha);
222    } else {
223        *alphaData = alpha;
224    }
225}
226
227SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
228    SkPDFArray* result = new SkPDFArray();
229    result->reserve(4);
230    result->appendName("Indexed");
231    result->appendName("DeviceRGB");
232    result->appendInt(table->count() - 1);
233
234    // Potentially, this could be represented in fewer bytes with a stream.
235    // Max size as a string is 1.5k.
236    SkString index;
237    for (int i = 0; i < table->count(); i++) {
238        char buf[3];
239        SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
240        buf[0] = SkGetPackedR32(color);
241        buf[1] = SkGetPackedG32(color);
242        buf[2] = SkGetPackedB32(color);
243        index.append(buf, 3);
244    }
245    result->append(new SkPDFString(index))->unref();
246    return result;
247}
248
249};  // namespace
250
251// static
252SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
253                                    const SkIRect& srcRect,
254                                    const SkPaint& paint) {
255    if (bitmap.getConfig() == SkBitmap::kNo_Config) {
256        return NULL;
257    }
258
259    SkStream* imageData = NULL;
260    SkStream* alphaData = NULL;
261    extractImageData(bitmap, srcRect, &imageData, &alphaData);
262    SkAutoUnref unrefImageData(imageData);
263    SkAutoUnref unrefAlphaData(alphaData);
264    if (!imageData) {
265        SkASSERT(!alphaData);
266        return NULL;
267    }
268
269    SkPDFImage* image =
270        new SkPDFImage(imageData, bitmap, srcRect, false, paint);
271
272    if (alphaData != NULL) {
273        image->addSMask(new SkPDFImage(alphaData, bitmap, srcRect, true,
274                                       paint))->unref();
275    }
276    return image;
277}
278
279SkPDFImage::~SkPDFImage() {
280    fResources.unrefAll();
281}
282
283SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
284    fResources.push(mask);
285    mask->ref();
286    insert("SMask", new SkPDFObjRef(mask))->unref();
287    return mask;
288}
289
290void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) {
291    GetResourcesHelper(&fResources, resourceList);
292}
293
294SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
295                       const SkIRect& srcRect, bool doingAlpha,
296                       const SkPaint& paint) {
297    this->setData(imageData);
298    SkBitmap::Config config = bitmap.getConfig();
299    bool alphaOnly = (config == SkBitmap::kA1_Config ||
300                      config == SkBitmap::kA8_Config);
301
302    insertName("Type", "XObject");
303    insertName("Subtype", "Image");
304
305    if (!doingAlpha && alphaOnly) {
306        // For alpha only images, we stretch a single pixel of black for
307        // the color/shape part.
308        SkRefPtr<SkPDFInt> one = new SkPDFInt(1);
309        one->unref();  // SkRefPtr and new both took a reference.
310        insert("Width", one.get());
311        insert("Height", one.get());
312    } else {
313        insertInt("Width", srcRect.width());
314        insertInt("Height", srcRect.height());
315    }
316
317    // if (!image mask) {
318    if (doingAlpha || alphaOnly) {
319        insertName("ColorSpace", "DeviceGray");
320    } else if (config == SkBitmap::kIndex8_Config ||
321        config == SkBitmap::kRLE_Index8_Config) {
322        insert("ColorSpace",
323               makeIndexedColorSpace(bitmap.getColorTable()))->unref();
324    } else {
325        insertName("ColorSpace", "DeviceRGB");
326    }
327    // }
328
329    int bitsPerComp = 8;
330    if (config == SkBitmap::kARGB_4444_Config) {
331        bitsPerComp = 4;
332    } else if (doingAlpha && config == SkBitmap::kA1_Config) {
333        bitsPerComp = 1;
334    }
335    insertInt("BitsPerComponent", bitsPerComp);
336
337    if (config == SkBitmap::kRGB_565_Config) {
338        SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
339        zeroVal->unref();  // SkRefPtr and new both took a reference.
340        SkRefPtr<SkPDFScalar> scale5Val =
341                new SkPDFScalar(8.2258f);  // 255/2^5-1
342        scale5Val->unref();  // SkRefPtr and new both took a reference.
343        SkRefPtr<SkPDFScalar> scale6Val =
344                new SkPDFScalar(4.0476f);  // 255/2^6-1
345        scale6Val->unref();  // SkRefPtr and new both took a reference.
346        SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
347        decodeValue->unref();  // SkRefPtr and new both took a reference.
348        decodeValue->reserve(6);
349        decodeValue->append(zeroVal.get());
350        decodeValue->append(scale5Val.get());
351        decodeValue->append(zeroVal.get());
352        decodeValue->append(scale6Val.get());
353        decodeValue->append(zeroVal.get());
354        decodeValue->append(scale5Val.get());
355        insert("Decode", decodeValue.get());
356    }
357}
358