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