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