1/*
2 * Copyright 2010 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkPDFImage.h"
9
10#include "SkBitmap.h"
11#include "SkColor.h"
12#include "SkColorPriv.h"
13#include "SkData.h"
14#include "SkFlate.h"
15#include "SkPDFCatalog.h"
16#include "SkRect.h"
17#include "SkStream.h"
18#include "SkString.h"
19#include "SkUnPreMultiply.h"
20
21static const int kNoColorTransform = 0;
22
23static bool skip_compression(SkPDFCatalog* catalog) {
24    return SkToBool(catalog->getDocumentFlags() &
25                    SkPDFDocument::kFavorSpeedOverSize_Flags);
26}
27
28static size_t get_uncompressed_size(const SkBitmap& bitmap,
29                                    const SkIRect& srcRect) {
30    switch (bitmap.colorType()) {
31        case kIndex_8_SkColorType:
32            return srcRect.width() * srcRect.height();
33        case kARGB_4444_SkColorType:
34            return ((srcRect.width() * 3 + 1) / 2) * srcRect.height();
35        case kRGB_565_SkColorType:
36            return srcRect.width() * 3 * srcRect.height();
37        case kRGBA_8888_SkColorType:
38        case kBGRA_8888_SkColorType:
39            return srcRect.width() * 3 * srcRect.height();
40        case kAlpha_8_SkColorType:
41            return 1;
42        default:
43            SkASSERT(false);
44            return 0;
45    }
46}
47
48static SkStream* extract_index8_image(const SkBitmap& bitmap,
49                                      const SkIRect& srcRect) {
50    const int rowBytes = srcRect.width();
51    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
52                                  (get_uncompressed_size(bitmap, srcRect)));
53    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
54
55    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
56        memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
57        dst += rowBytes;
58    }
59    return stream;
60}
61
62static SkStream* extract_argb4444_data(const SkBitmap& bitmap,
63                                       const SkIRect& srcRect,
64                                       bool extractAlpha,
65                                       bool* isOpaque,
66                                       bool* isTransparent) {
67    SkStream* stream;
68    uint8_t* dst = NULL;
69    if (extractAlpha) {
70        const int alphaRowBytes = (srcRect.width() + 1) / 2;
71        stream = SkNEW_ARGS(SkMemoryStream,
72                            (alphaRowBytes * srcRect.height()));
73    } else {
74        stream = SkNEW_ARGS(SkMemoryStream,
75                            (get_uncompressed_size(bitmap, srcRect)));
76    }
77    dst = (uint8_t*)stream->getMemoryBase();
78
79    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
80        uint16_t* src = bitmap.getAddr16(0, y);
81        int x;
82        for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
83            if (extractAlpha) {
84                dst[0] = (SkGetPackedA4444(src[x]) << 4) |
85                    SkGetPackedA4444(src[x + 1]);
86                *isOpaque &= dst[0] == SK_AlphaOPAQUE;
87                *isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
88                dst++;
89            } else {
90                dst[0] = (SkGetPackedR4444(src[x]) << 4) |
91                    SkGetPackedG4444(src[x]);
92                dst[1] = (SkGetPackedB4444(src[x]) << 4) |
93                    SkGetPackedR4444(src[x + 1]);
94                dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
95                    SkGetPackedB4444(src[x + 1]);
96                dst += 3;
97            }
98        }
99        if (srcRect.width() & 1) {
100            if (extractAlpha) {
101                dst[0] = (SkGetPackedA4444(src[x]) << 4);
102                *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0);
103                *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0);
104                dst++;
105
106            } else {
107                dst[0] = (SkGetPackedR4444(src[x]) << 4) |
108                    SkGetPackedG4444(src[x]);
109                dst[1] = (SkGetPackedB4444(src[x]) << 4);
110                dst += 2;
111            }
112        }
113    }
114    return stream;
115}
116
117static SkStream* extract_rgb565_image(const SkBitmap& bitmap,
118                                      const SkIRect& srcRect) {
119    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
120                                  (get_uncompressed_size(bitmap,
121                                                     srcRect)));
122    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
123    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
124        uint16_t* src = bitmap.getAddr16(0, y);
125        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
126            dst[0] = SkGetPackedR16(src[x]);
127            dst[1] = SkGetPackedG16(src[x]);
128            dst[2] = SkGetPackedB16(src[x]);
129            dst += 3;
130        }
131    }
132    return stream;
133}
134
135static SkStream* extract_argb8888_data(const SkBitmap& bitmap,
136                                       const SkIRect& srcRect,
137                                       bool extractAlpha,
138                                       bool* isOpaque,
139                                       bool* isTransparent) {
140    SkStream* stream;
141    if (extractAlpha) {
142        stream = SkNEW_ARGS(SkMemoryStream,
143                            (srcRect.width() * srcRect.height()));
144    } else {
145        stream = SkNEW_ARGS(SkMemoryStream,
146                            (get_uncompressed_size(bitmap, srcRect)));
147    }
148    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
149
150    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
151        uint32_t* src = bitmap.getAddr32(0, y);
152        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
153            if (extractAlpha) {
154                dst[0] = SkGetPackedA32(src[x]);
155                *isOpaque &= dst[0] == SK_AlphaOPAQUE;
156                *isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
157                dst++;
158            } else {
159                dst[0] = SkGetPackedR32(src[x]);
160                dst[1] = SkGetPackedG32(src[x]);
161                dst[2] = SkGetPackedB32(src[x]);
162                dst += 3;
163            }
164        }
165    }
166    return stream;
167}
168
169static SkStream* extract_a8_alpha(const SkBitmap& bitmap,
170                                  const SkIRect& srcRect,
171                                  bool* isOpaque,
172                                  bool* isTransparent) {
173    const int alphaRowBytes = srcRect.width();
174    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
175                                  (alphaRowBytes * srcRect.height()));
176    uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
177
178    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
179        uint8_t* src = bitmap.getAddr8(0, y);
180        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
181            alphaDst[0] = src[x];
182            *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
183            *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
184            alphaDst++;
185        }
186    }
187    return stream;
188}
189
190static SkStream* create_black_image() {
191    SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1));
192    ((uint8_t*)stream->getMemoryBase())[0] = 0;
193    return stream;
194}
195
196/**
197 * Extract either the color or image data from a SkBitmap into a SkStream.
198 * @param bitmap        Bitmap to extract data from.
199 * @param srcRect       Region in the bitmap to extract.
200 * @param extractAlpha  Set to true to extract the alpha data or false to
201 *                      extract the color data.
202 * @param isTransparent Pointer to a bool to output whether the alpha is
203 *                      completely transparent. May be NULL. Only valid when
204 *                      extractAlpha == true.
205 * @return              Unencoded image data, or NULL if either data was not
206 *                      available or alpha data was requested but the image was
207 *                      entirely transparent or opaque.
208 */
209static SkStream* extract_image_data(const SkBitmap& bitmap,
210                                    const SkIRect& srcRect,
211                                    bool extractAlpha, bool* isTransparent) {
212    SkColorType colorType = bitmap.colorType();
213    if (extractAlpha && (kIndex_8_SkColorType == colorType ||
214                         kRGB_565_SkColorType == colorType)) {
215        if (isTransparent != NULL) {
216            *isTransparent = false;
217        }
218        return NULL;
219    }
220    bool isOpaque = true;
221    bool transparent = extractAlpha;
222    SkStream* stream = NULL;
223
224    bitmap.lockPixels();
225    switch (colorType) {
226        case kIndex_8_SkColorType:
227            if (!extractAlpha) {
228                stream = extract_index8_image(bitmap, srcRect);
229            }
230            break;
231        case kARGB_4444_SkColorType:
232            stream = extract_argb4444_data(bitmap, srcRect, extractAlpha,
233                                           &isOpaque, &transparent);
234            break;
235        case kRGB_565_SkColorType:
236            if (!extractAlpha) {
237                stream = extract_rgb565_image(bitmap, srcRect);
238            }
239            break;
240        case kN32_SkColorType:
241            stream = extract_argb8888_data(bitmap, srcRect, extractAlpha,
242                                           &isOpaque, &transparent);
243            break;
244        case kAlpha_8_SkColorType:
245            if (!extractAlpha) {
246                stream = create_black_image();
247            } else {
248                stream = extract_a8_alpha(bitmap, srcRect,
249                                          &isOpaque, &transparent);
250            }
251            break;
252        default:
253            SkASSERT(false);
254    }
255    bitmap.unlockPixels();
256
257    if (isTransparent != NULL) {
258        *isTransparent = transparent;
259    }
260    if (extractAlpha && (transparent || isOpaque)) {
261        SkSafeUnref(stream);
262        return NULL;
263    }
264    return stream;
265}
266
267static SkPDFArray* make_indexed_color_space(SkColorTable* table) {
268    SkPDFArray* result = new SkPDFArray();
269    result->reserve(4);
270    result->appendName("Indexed");
271    result->appendName("DeviceRGB");
272    result->appendInt(table->count() - 1);
273
274    // Potentially, this could be represented in fewer bytes with a stream.
275    // Max size as a string is 1.5k.
276    SkString index;
277    for (int i = 0; i < table->count(); i++) {
278        char buf[3];
279        SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
280        buf[0] = SkGetPackedR32(color);
281        buf[1] = SkGetPackedG32(color);
282        buf[2] = SkGetPackedB32(color);
283        index.append(buf, 3);
284    }
285    result->append(new SkPDFString(index))->unref();
286    return result;
287}
288
289/**
290 * Removes the alpha component of an ARGB color (including unpremultiply) while
291 * keeping the output in the same format as the input.
292 */
293static uint32_t remove_alpha_argb8888(uint32_t pmColor) {
294    SkColor color = SkUnPreMultiply::PMColorToColor(pmColor);
295    return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
296                               SkColorGetR(color),
297                               SkColorGetG(color),
298                               SkColorGetB(color));
299}
300
301static uint16_t remove_alpha_argb4444(uint16_t pmColor) {
302    return SkPixel32ToPixel4444(
303            remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor)));
304}
305
306static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap,
307                                                int xOrig, int yOrig) {
308    uint8_t count = 0;
309    uint16_t r = 0;
310    uint16_t g = 0;
311    uint16_t b = 0;
312
313    for (int y = yOrig - 1; y <= yOrig + 1; y++) {
314        if (y < 0 || y >= bitmap.height()) {
315            continue;
316        }
317        uint32_t* src = bitmap.getAddr32(0, y);
318        for (int x = xOrig - 1; x <= xOrig + 1; x++) {
319            if (x < 0 || x >= bitmap.width()) {
320                continue;
321            }
322            if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) {
323                uint32_t color = remove_alpha_argb8888(src[x]);
324                r += SkGetPackedR32(color);
325                g += SkGetPackedG32(color);
326                b += SkGetPackedB32(color);
327                count++;
328            }
329        }
330    }
331
332    if (count == 0) {
333        return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
334    } else {
335        return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
336                                   r / count, g / count, b / count);
337    }
338}
339
340static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap,
341                                                int xOrig, int yOrig) {
342    uint8_t count = 0;
343    uint8_t r = 0;
344    uint8_t g = 0;
345    uint8_t b = 0;
346
347    for (int y = yOrig - 1; y <= yOrig + 1; y++) {
348        if (y < 0 || y >= bitmap.height()) {
349            continue;
350        }
351        uint16_t* src = bitmap.getAddr16(0, y);
352        for (int x = xOrig - 1; x <= xOrig + 1; x++) {
353            if (x < 0 || x >= bitmap.width()) {
354                continue;
355            }
356            if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) {
357                uint16_t color = remove_alpha_argb4444(src[x]);
358                r += SkGetPackedR4444(color);
359                g += SkGetPackedG4444(color);
360                b += SkGetPackedB4444(color);
361                count++;
362            }
363        }
364    }
365
366    if (count == 0) {
367        return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0);
368    } else {
369        return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F,
370                                   r / count, g / count, b / count);
371    }
372}
373
374static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap,
375                                     const SkIRect& srcRect) {
376    SkBitmap outBitmap;
377    outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height()));
378    int dstRow = 0;
379
380    outBitmap.lockPixels();
381    bitmap.lockPixels();
382    switch (bitmap.colorType()) {
383        case kARGB_4444_SkColorType: {
384            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
385                uint16_t* dst = outBitmap.getAddr16(0, dstRow);
386                uint16_t* src = bitmap.getAddr16(0, y);
387                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
388                    uint8_t a = SkGetPackedA4444(src[x]);
389                    // It is necessary to average the color component of
390                    // transparent pixels with their surrounding neighbors
391                    // since the PDF renderer may separately re-sample the
392                    // alpha and color channels when the image is not
393                    // displayed at its native resolution. Since an alpha of
394                    // zero gives no information about the color component,
395                    // the pathological case is a white image with sharp
396                    // transparency bounds - the color channel goes to black,
397                    // and the should-be-transparent pixels are rendered
398                    // as grey because of the separate soft mask and color
399                    // resizing.
400                    if (a == (SK_AlphaTRANSPARENT & 0x0F)) {
401                        *dst = get_argb4444_neighbor_avg_color(bitmap, x, y);
402                    } else {
403                        *dst = remove_alpha_argb4444(src[x]);
404                    }
405                    dst++;
406                }
407                dstRow++;
408            }
409            break;
410        }
411        case kN32_SkColorType: {
412            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
413                uint32_t* dst = outBitmap.getAddr32(0, dstRow);
414                uint32_t* src = bitmap.getAddr32(0, y);
415                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
416                    uint8_t a = SkGetPackedA32(src[x]);
417                    if (a == SK_AlphaTRANSPARENT) {
418                        *dst = get_argb8888_neighbor_avg_color(bitmap, x, y);
419                    } else {
420                        *dst = remove_alpha_argb8888(src[x]);
421                    }
422                    dst++;
423                }
424                dstRow++;
425            }
426            break;
427        }
428        default:
429            SkASSERT(false);
430    }
431    bitmap.unlockPixels();
432    outBitmap.unlockPixels();
433
434    outBitmap.setImmutable();
435
436    return outBitmap;
437}
438
439// static
440SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
441                                    const SkIRect& srcRect,
442                                    SkPicture::EncodeBitmap encoder) {
443    if (bitmap.colorType() == kUnknown_SkColorType) {
444        return NULL;
445    }
446
447    bool isTransparent = false;
448    SkAutoTUnref<SkStream> alphaData;
449    if (!bitmap.isOpaque()) {
450        // Note that isOpaque is not guaranteed to return false for bitmaps
451        // with alpha support but a completely opaque alpha channel,
452        // so alphaData may still be NULL if we have a completely opaque
453        // (or transparent) bitmap.
454        alphaData.reset(
455                extract_image_data(bitmap, srcRect, true, &isTransparent));
456    }
457    if (isTransparent) {
458        return NULL;
459    }
460
461    SkPDFImage* image;
462    SkColorType colorType = bitmap.colorType();
463    if (alphaData.get() != NULL && (kN32_SkColorType == colorType ||
464                                    kARGB_4444_SkColorType == colorType)) {
465        SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect);
466        image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false,
467                           SkIRect::MakeWH(srcRect.width(), srcRect.height()),
468                           encoder));
469    } else {
470        image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect, encoder));
471    }
472    if (alphaData.get() != NULL) {
473        SkAutoTUnref<SkPDFImage> mask(
474                SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap,
475                                        true, srcRect, NULL)));
476        image->addSMask(mask);
477    }
478
479    return image;
480}
481
482SkPDFImage::~SkPDFImage() {
483    fResources.unrefAll();
484}
485
486SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
487    fResources.push(mask);
488    mask->ref();
489    insert("SMask", new SkPDFObjRef(mask))->unref();
490    return mask;
491}
492
493void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
494                              SkTSet<SkPDFObject*>* newResourceObjects) {
495    GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
496}
497
498SkPDFImage::SkPDFImage(SkStream* stream,
499                       const SkBitmap& bitmap,
500                       bool isAlpha,
501                       const SkIRect& srcRect,
502                       SkPicture::EncodeBitmap encoder)
503    : fIsAlpha(isAlpha),
504      fSrcRect(srcRect),
505      fEncoder(encoder) {
506
507    if (bitmap.isImmutable()) {
508        fBitmap = bitmap;
509    } else {
510        bitmap.deepCopyTo(&fBitmap);
511        fBitmap.setImmutable();
512    }
513
514    if (stream != NULL) {
515        setData(stream);
516        fStreamValid = true;
517    } else {
518        fStreamValid = false;
519    }
520
521    SkColorType colorType = fBitmap.colorType();
522
523    insertName("Type", "XObject");
524    insertName("Subtype", "Image");
525
526    bool alphaOnly = (kAlpha_8_SkColorType == colorType);
527
528    if (!isAlpha && alphaOnly) {
529        // For alpha only images, we stretch a single pixel of black for
530        // the color/shape part.
531        SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
532        insert("Width", one.get());
533        insert("Height", one.get());
534    } else {
535        insertInt("Width", fSrcRect.width());
536        insertInt("Height", fSrcRect.height());
537    }
538
539    if (isAlpha || alphaOnly) {
540        insertName("ColorSpace", "DeviceGray");
541    } else if (kIndex_8_SkColorType == colorType) {
542        SkAutoLockPixels alp(fBitmap);
543        insert("ColorSpace",
544               make_indexed_color_space(fBitmap.getColorTable()))->unref();
545    } else {
546        insertName("ColorSpace", "DeviceRGB");
547    }
548
549    int bitsPerComp = 8;
550    if (kARGB_4444_SkColorType == colorType) {
551        bitsPerComp = 4;
552    }
553    insertInt("BitsPerComponent", bitsPerComp);
554
555    if (kRGB_565_SkColorType == colorType) {
556        SkASSERT(!isAlpha);
557        SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
558        SkAutoTUnref<SkPDFScalar> scale5Val(
559                new SkPDFScalar(8.2258f));  // 255/2^5-1
560        SkAutoTUnref<SkPDFScalar> scale6Val(
561                new SkPDFScalar(4.0476f));  // 255/2^6-1
562        SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
563        decodeValue->reserve(6);
564        decodeValue->append(zeroVal.get());
565        decodeValue->append(scale5Val.get());
566        decodeValue->append(zeroVal.get());
567        decodeValue->append(scale6Val.get());
568        decodeValue->append(zeroVal.get());
569        decodeValue->append(scale5Val.get());
570        insert("Decode", decodeValue.get());
571    }
572}
573
574SkPDFImage::SkPDFImage(SkPDFImage& pdfImage)
575    : SkPDFStream(pdfImage),
576      fBitmap(pdfImage.fBitmap),
577      fIsAlpha(pdfImage.fIsAlpha),
578      fSrcRect(pdfImage.fSrcRect),
579      fEncoder(pdfImage.fEncoder),
580      fStreamValid(pdfImage.fStreamValid) {
581    // Nothing to do here - the image params are already copied in SkPDFStream's
582    // constructor, and the bitmap will be regenerated and encoded in
583    // populate.
584}
585
586bool SkPDFImage::populate(SkPDFCatalog* catalog) {
587    if (getState() == kUnused_State) {
588        // Initializing image data for the first time.
589        SkDynamicMemoryWStream dctCompressedWStream;
590        if (!skip_compression(catalog) && fEncoder &&
591                get_uncompressed_size(fBitmap, fSrcRect) > 1) {
592            SkBitmap subset;
593            // Extract subset
594            if (!fBitmap.extractSubset(&subset, fSrcRect)) {
595                return false;
596            }
597            size_t pixelRefOffset = 0;
598            SkAutoTUnref<SkData> data(fEncoder(&pixelRefOffset, subset));
599            if (data.get() && data->size() < get_uncompressed_size(fBitmap,
600                                                                   fSrcRect)) {
601                SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream,
602                                                         (data)));
603                setData(stream.get());
604
605                insertName("Filter", "DCTDecode");
606                insertInt("ColorTransform", kNoColorTransform);
607                insertInt("Length", getData()->getLength());
608                setState(kCompressed_State);
609                return true;
610            }
611        }
612        // Fallback method
613        if (!fStreamValid) {
614            SkAutoTUnref<SkStream> stream(
615                    extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL));
616            setData(stream);
617            fStreamValid = true;
618        }
619        return INHERITED::populate(catalog);
620    } else if (getState() == kNoCompression_State &&
621            !skip_compression(catalog) &&
622            (SkFlate::HaveFlate() || fEncoder)) {
623        // Compression has not been requested when the stream was first created,
624        // but the new catalog wants it compressed.
625        if (!getSubstitute()) {
626            SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this));
627            setSubstitute(substitute);
628            catalog->setSubstitute(this, substitute);
629        }
630        return false;
631    }
632    return true;
633}
634