diff options
Diffstat (limited to 'src/pdf')
-rw-r--r-- | src/pdf/SkPDFBitmap.cpp | 415 | ||||
-rw-r--r-- | src/pdf/SkPDFBitmap.h | 11 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 12 | ||||
-rw-r--r-- | src/pdf/SkPDFImage.cpp | 727 | ||||
-rw-r--r-- | src/pdf/SkPDFImage.h | 91 |
5 files changed, 266 insertions, 990 deletions
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp index 668f7dede0..486dac44aa 100644 --- a/src/pdf/SkPDFBitmap.cpp +++ b/src/pdf/SkPDFBitmap.cpp @@ -25,114 +25,214 @@ static void pdf_stream_end(SkWStream* stream) { stream->write(streamEnd, strlen(streamEnd)); } -static size_t pixel_count(const SkBitmap& bm) { - return SkToSizeT(bm.width()) * SkToSizeT(bm.height()); -} +//////////////////////////////////////////////////////////////////////////////// // write a single byte to a stream n times. static void fill_stream(SkWStream* out, char value, size_t n) { char buffer[4096]; memset(buffer, value, sizeof(buffer)); - while (n) { - size_t k = SkTMin(n, sizeof(buffer)); - out->write(buffer, k); - n -= k; + for (size_t i = 0; i < n / sizeof(buffer); ++i) { + out->write(buffer, sizeof(buffer)); } + out->write(buffer, n % sizeof(buffer)); } -static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap, - int xOrig, - int yOrig) { - SkASSERT(kN32_SkColorType == bitmap.colorType()); - SkASSERT(bitmap.getPixels()); - uint8_t count = 0; - unsigned r = 0; - unsigned g = 0; - unsigned b = 0; - for (int y = yOrig - 1; y <= yOrig + 1; ++y) { - if (y < 0 || y >= bitmap.height()) { - continue; - } - uint32_t* src = bitmap.getAddr32(0, y); - for (int x = xOrig - 1; x <= xOrig + 1; ++x) { - if (x < 0 || x >= bitmap.width()) { - continue; - } - SkPMColor pmColor = src[x]; - U8CPU alpha = SkGetPackedA32(pmColor); - if (alpha != SK_AlphaTRANSPARENT) { - uint32_t s = SkUnPreMultiply::GetScale(alpha); - r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor)); - g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor)); - b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor)); - ++count; - } +// unpremultiply and extract R, G, B components. +static void pmcolor_to_rgb24(SkPMColor pmColor, uint8_t* rgb) { + uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor)); + rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor)); + rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor)); + rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor)); +} + +/* It is necessary to average the color component of transparent + pixels with their surrounding neighbors since the PDF renderer may + separately re-sample the alpha and color channels when the image is + not displayed at its native resolution. Since an alpha of zero + gives no information about the color component, the pathological + case is a white image with sharp transparency bounds - the color + channel goes to black, and the should-be-transparent pixels are + rendered as grey because of the separate soft mask and color + resizing. e.g.: gm/bitmappremul.cpp */ +static void get_neighbor_avg_color(const SkBitmap& bm, + int xOrig, + int yOrig, + uint8_t rgb[3]) { + SkASSERT(kN32_SkColorType == bm.colorType()); + unsigned a = 0, r = 0, g = 0, b = 0; + // Clamp the range to the edge of the bitmap. + int ymin = SkTMax(0, yOrig - 1); + int ymax = SkTMin(yOrig + 1, bm.height() - 1); + int xmin = SkTMax(0, xOrig - 1); + int xmax = SkTMin(xOrig + 1, bm.width() - 1); + for (int y = ymin; y <= ymax; ++y) { + SkPMColor* scanline = bm.getAddr32(0, y); + for (int x = xmin; x <= xmax; ++x) { + SkPMColor pmColor = scanline[x]; + a += SkGetPackedA32(pmColor); + r += SkGetPackedR32(pmColor); + g += SkGetPackedG32(pmColor); + b += SkGetPackedB32(pmColor); } } - if (count == 0) { - return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); + if (a > 0) { + rgb[0] = SkToU8(255 * r / a); + rgb[1] = SkToU8(255 * g / a); + rgb[2] = SkToU8(255 * b / a); } else { - return SkPackARGB32NoCheck( - SK_AlphaOPAQUE, r / count, g / count, b / count); + rgb[0] = rgb[1] = rgb[2] = 0; } } -static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) { - SkASSERT(kN32_SkColorType == bm.colorType()); - if (!bm.getPixels()) { - fill_stream(out, '\xFF', 3 * pixel_count(bm)); +static size_t pixel_count(const SkBitmap& bm) { + return SkToSizeT(bm.width()) * SkToSizeT(bm.height()); +} + +static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) { + if (input.colorType() != kARGB_4444_SkColorType) { + return input; + } + // ARGB_4444 is rarely used, so we can do a wasteful tmp copy. + SkAssertResult(input.copyTo(copy, kN32_SkColorType)); + copy->setImmutable(); + return *copy; +} + +static size_t pdf_color_component_count(SkColorType ct) { + switch (ct) { + case kN32_SkColorType: + case kRGB_565_SkColorType: + case kARGB_4444_SkColorType: + return 3; + case kAlpha_8_SkColorType: + case kIndex_8_SkColorType: + case kGray_8_SkColorType: + return 1; + case kUnknown_SkColorType: + default: + SkDEBUGFAIL("unexpected color type"); + return 0; + } +} + +static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) { + if (!bitmap.getPixels()) { + size_t size = pixel_count(bitmap) * + pdf_color_component_count(bitmap.colorType()); + fill_stream(out, '\x00', size); return; } - size_t scanlineLength = 3 * bm.width(); - SkAutoTMalloc<uint8_t> scanline(scanlineLength); - for (int y = 0; y < bm.height(); ++y) { - uint8_t* dst = scanline.get(); - const SkPMColor* src = bm.getAddr32(0, y); - for (int x = 0; x < bm.width(); ++x) { - SkPMColor color = *src++; - U8CPU alpha = SkGetPackedA32(color); - if (alpha != SK_AlphaTRANSPARENT) { - uint32_t s = SkUnPreMultiply::GetScale(alpha); - *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color)); - *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color)); - *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color)); - } else { - /* It is necessary to average the color component of - transparent pixels with their surrounding neighbors - since the PDF renderer may separately re-sample the - alpha and color channels when the image is not - displayed at its native resolution. Since an alpha - of zero gives no information about the color - component, the pathological case is a white image - with sharp transparency bounds - the color channel - goes to black, and the should-be-transparent pixels - are rendered as grey because of the separate soft - mask and color resizing. e.g.: gm/bitmappremul.cpp */ - color = get_pmcolor_neighbor_avg_color(bm, x, y); - *dst++ = SkGetPackedR32(color); - *dst++ = SkGetPackedG32(color); - *dst++ = SkGetPackedB32(color); + SkBitmap copy; + const SkBitmap& bm = not4444(bitmap, ©); + SkAutoLockPixels autoLockPixels(bm); + switch (bm.colorType()) { + case kN32_SkColorType: { + SkASSERT(3 == pdf_color_component_count(bitmap.colorType())); + SkAutoTMalloc<uint8_t> scanline(3 * bm.width()); + for (int y = 0; y < bm.height(); ++y) { + const SkPMColor* src = bm.getAddr32(0, y); + uint8_t* dst = scanline.get(); + for (int x = 0; x < bm.width(); ++x) { + SkPMColor color = *src++; + U8CPU alpha = SkGetPackedA32(color); + if (alpha != SK_AlphaTRANSPARENT) { + pmcolor_to_rgb24(color, dst); + } else { + get_neighbor_avg_color(bm, x, y, dst); + } + dst += 3; + } + out->write(scanline.get(), 3 * bm.width()); } + return; } - out->write(scanline.get(), scanlineLength); + case kRGB_565_SkColorType: { + SkASSERT(3 == pdf_color_component_count(bitmap.colorType())); + SkAutoTMalloc<uint8_t> scanline(3 * bm.width()); + for (int y = 0; y < bm.height(); ++y) { + const uint16_t* src = bm.getAddr16(0, y); + uint8_t* dst = scanline.get(); + for (int x = 0; x < bm.width(); ++x) { + U16CPU color565 = *src++; + *dst++ = SkPacked16ToR32(color565); + *dst++ = SkPacked16ToG32(color565); + *dst++ = SkPacked16ToB32(color565); + } + out->write(scanline.get(), 3 * bm.width()); + } + return; + } + case kAlpha_8_SkColorType: + SkASSERT(1 == pdf_color_component_count(bitmap.colorType())); + fill_stream(out, '\x00', pixel_count(bm)); + return; + case kGray_8_SkColorType: + case kIndex_8_SkColorType: + SkASSERT(1 == pdf_color_component_count(bitmap.colorType())); + // these two formats need no transformation to serialize. + for (int y = 0; y < bm.height(); ++y) { + out->write(bm.getAddr8(0, y), bm.width()); + } + return; + case kUnknown_SkColorType: + case kARGB_4444_SkColorType: + default: + SkDEBUGFAIL("unexpected color type"); } } -static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { - SkASSERT(kN32_SkColorType == bm.colorType()); - if (!bm.getPixels()) { - fill_stream(out, '\xFF', pixel_count(bm)); +//////////////////////////////////////////////////////////////////////////////// + +static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) { + if (!bitmap.getPixels()) { + fill_stream(out, '\xFF', pixel_count(bitmap)); return; } - size_t scanlineLength = bm.width(); - SkAutoTMalloc<uint8_t> scanline(scanlineLength); - for (int y = 0; y < bm.height(); ++y) { - uint8_t* dst = scanline.get(); - const SkPMColor* src = bm.getAddr32(0, y); - for (int x = 0; x < bm.width(); ++x) { - *dst++ = SkGetPackedA32(*src++); + SkBitmap copy; + const SkBitmap& bm = not4444(bitmap, ©); + SkAutoLockPixels autoLockPixels(bm); + switch (bm.colorType()) { + case kN32_SkColorType: { + SkAutoTMalloc<uint8_t> scanline(bm.width()); + for (int y = 0; y < bm.height(); ++y) { + uint8_t* dst = scanline.get(); + const SkPMColor* src = bm.getAddr32(0, y); + for (int x = 0; x < bm.width(); ++x) { + *dst++ = SkGetPackedA32(*src++); + } + out->write(scanline.get(), bm.width()); + } + return; } - out->write(scanline.get(), scanlineLength); + case kAlpha_8_SkColorType: + for (int y = 0; y < bm.height(); ++y) { + out->write(bm.getAddr8(0, y), bm.width()); + } + return; + case kIndex_8_SkColorType: { + SkColorTable* ct = bm.getColorTable(); + SkASSERT(ct); + SkAutoTMalloc<uint8_t> scanline(bm.width()); + for (int y = 0; y < bm.height(); ++y) { + uint8_t* dst = scanline.get(); + const uint8_t* src = bm.getAddr8(0, y); + for (int x = 0; x < bm.width(); ++x) { + *dst++ = SkGetPackedA32((*ct)[*src++]); + } + out->write(scanline.get(), bm.width()); + } + return; + } + case kRGB_565_SkColorType: + case kGray_8_SkColorType: + SkDEBUGFAIL("color type has no alpha"); + return; + case kARGB_4444_SkColorType: + SkDEBUGFAIL("4444 color type should have been converted to N32"); + return; + case kUnknown_SkColorType: + default: + SkDEBUGFAIL("unexpected color type"); } } @@ -145,49 +245,40 @@ public: PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} ~PDFAlphaBitmap() {} void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; - void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {} private: const SkBitmap fBitmap; - void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; + void emitDict(SkWStream*, SkPDFCatalog*, size_t) const; }; void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { SkAutoLockPixels autoLockPixels(fBitmap); + SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || + fBitmap.getColorTable()); -#ifndef SK_NO_FLATE // Write to a temporary buffer to get the compressed length. SkDynamicMemoryWStream buffer; SkDeflateWStream deflateWStream(&buffer); - pmcolor_alpha_to_a8(fBitmap, &deflateWStream); + bitmap_alpha_to_a8(fBitmap, &deflateWStream); deflateWStream.finalize(); // call before detachAsStream(). SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); - this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); + this->emitDict(stream, catalog, asset->getLength()); pdf_stream_begin(stream); stream->writeStream(asset.get(), asset->getLength()); pdf_stream_end(stream); -#else - this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false); - pdf_stream_begin(stream); - pmcolor_alpha_to_a8(fBitmap, stream); - pdf_stream_end(stream); -#endif // SK_NO_FLATE } void PDFAlphaBitmap::emitDict(SkWStream* stream, SkPDFCatalog* catalog, - size_t length, - bool deflate) const { + size_t length) const { SkPDFDict pdfDict("XObject"); pdfDict.insertName("Subtype", "Image"); pdfDict.insertInt("Width", fBitmap.width()); pdfDict.insertInt("Height", fBitmap.height()); pdfDict.insertName("ColorSpace", "DeviceGray"); pdfDict.insertInt("BitsPerComponent", 8); - if (deflate) { - pdfDict.insertName("Filter", "FlateDecode"); - } + pdfDict.insertName("Filter", "FlateDecode"); pdfDict.insertInt("Length", length); pdfDict.emitObject(stream, catalog); } @@ -198,50 +289,81 @@ void PDFAlphaBitmap::emitDict(SkWStream* stream, void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet, SkPDFCatalog* catalog) const { if (fSMask.get()) { - resourceSet->add(fSMask.get()); + if (resourceSet->add(fSMask.get())) { + fSMask->addResources(resourceSet, catalog); + } } } void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { SkAutoLockPixels autoLockPixels(fBitmap); + SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || + fBitmap.getColorTable()); -#ifndef SK_NO_FLATE // Write to a temporary buffer to get the compressed length. SkDynamicMemoryWStream buffer; SkDeflateWStream deflateWStream(&buffer); - pmcolor_to_rgb24(fBitmap, &deflateWStream); + bitmap_to_pdf_pixels(fBitmap, &deflateWStream); deflateWStream.finalize(); // call before detachAsStream(). SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); - this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); + this->emitDict(stream, catalog, asset->getLength()); pdf_stream_begin(stream); stream->writeStream(asset.get(), asset->getLength()); pdf_stream_end(stream); -#else - this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false); - pdf_stream_begin(stream); - pmcolor_to_rgb24(fBitmap, stream); - pdf_stream_end(stream); - return; -#endif // SK_NO_FLATE +} + +static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { + SkPDFArray* result = SkNEW(SkPDFArray); + result->reserve(4); + result->appendName("Indexed"); + result->appendName("DeviceRGB"); + SkASSERT(table); + if (table->count() < 1) { + result->appendInt(0); + char shortTableArray[3] = {0, 0, 0}; + SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray)); + result->append(new SkPDFString(tableString))->unref(); + return result; + } + result->appendInt(table->count() - 1); // maximum color index. + + // Potentially, this could be represented in fewer bytes with a stream. + // Max size as a string is 1.5k. + char tableArray[256 * 3]; + SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray)); + uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray); + const SkPMColor* colors = table->readColors(); + for (int i = 0; i < table->count(); i++) { + pmcolor_to_rgb24(colors[i], tablePtr); + tablePtr += 3; + } + SkString tableString(tableArray, 3 * table->count()); + result->append(new SkPDFString(tableString))->unref(); + return result; } void SkPDFBitmap::emitDict(SkWStream* stream, SkPDFCatalog* catalog, - size_t length, - bool deflate) const { + size_t length) const { SkPDFDict pdfDict("XObject"); pdfDict.insertName("Subtype", "Image"); pdfDict.insertInt("Width", fBitmap.width()); pdfDict.insertInt("Height", fBitmap.height()); - pdfDict.insertName("ColorSpace", "DeviceRGB"); + if (fBitmap.colorType() == kIndex_8_SkColorType) { + SkASSERT(1 == pdf_color_component_count(fBitmap.colorType())); + pdfDict.insert("ColorSpace", make_indexed_color_space( + fBitmap.getColorTable()))->unref(); + } else if (1 == pdf_color_component_count(fBitmap.colorType())) { + pdfDict.insertName("ColorSpace", "DeviceGray"); + } else { + pdfDict.insertName("ColorSpace", "DeviceRGB"); + } pdfDict.insertInt("BitsPerComponent", 8); if (fSMask) { pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); } - if (deflate) { - pdfDict.insertName("Filter", "FlateDecode"); - } + pdfDict.insertName("Filter", "FlateDecode"); pdfDict.insertInt("Length", length); pdfDict.emitObject(stream, catalog); } @@ -253,64 +375,35 @@ SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, SkPDFBitmap::~SkPDFBitmap() {} //////////////////////////////////////////////////////////////////////////////// -static bool is_transparent(const SkBitmap& bm) { - SkAutoLockPixels autoLockPixels(bm); - if (NULL == bm.getPixels()) { - return true; - } - SkASSERT(kN32_SkColorType == bm.colorType()); - for (int y = 0; y < bm.height(); ++y) { - U8CPU alpha = 0; - const SkPMColor* src = bm.getAddr32(0, y); - for (int x = 0; x < bm.width(); ++x) { - alpha |= SkGetPackedA32(*src++); - } - if (alpha) { - return false; - } + +static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) { + if (bm.isImmutable()) { + return bm; } - return true; + bm.copyTo(copy); + copy->setImmutable(); + return *copy; } -SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, - const SkBitmap& bitmap, - const SkIRect& subset) { +SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) { SkASSERT(canon); - if (kN32_SkColorType != bitmap.colorType()) { - // TODO(halcanary): support other colortypes. - return NULL; - } - SkBitmap bm; - // Should extractSubset be done by the SkPDFDevice? - if (!bitmap.extractSubset(&bm, subset)) { + if (!SkColorTypeIsValid(bitmap.colorType()) || + kUnknown_SkColorType == bitmap.colorType()) { return NULL; } + SkBitmap copy; + const SkBitmap& bm = immutable_bitmap(bitmap, ©); if (bm.drawsNothing()) { return NULL; } - if (!bm.isImmutable()) { - SkBitmap copy; - if (!bm.copyTo(©)) { - return NULL; - } - copy.setImmutable(); - bm = copy; - } - - SkPDFBitmap* pdfBitmap = canon->findBitmap(bm); - if (pdfBitmap) { - return SkRef(pdfBitmap); + if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) { + return SkRef(canonBitmap); } SkPDFObject* smask = NULL; if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { - if (is_transparent(bm)) { - return NULL; - } - // PDFAlphaBitmaps do not get directly canonicalized (they - // are refed by the SkPDFBitmap). smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); } - pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask)); + SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask)); canon->addBitmap(pdfBitmap); return pdfBitmap; } diff --git a/src/pdf/SkPDFBitmap.h b/src/pdf/SkPDFBitmap.h index 45a8aa6350..02a79df24e 100644 --- a/src/pdf/SkPDFBitmap.h +++ b/src/pdf/SkPDFBitmap.h @@ -17,18 +17,15 @@ class SkPDFCanon; * It is designed to use a minimal amout of memory, aside from refing * the bitmap's pixels, and its emitObject() does not cache any data. * - * As of now, it only supports 8888 bitmaps (the most common case). + * If !bitmap.isImmutable(), then a copy of the bitmap must be made; + * there is no way around this. * * The SkPDFBitmap::Create function will check the canon for duplicates. */ class SkPDFBitmap : public SkPDFObject { public: // Returns NULL on unsupported bitmap; - // TODO(halcanary): support other bitmap colortypes and replace - // SkPDFImage. - static SkPDFBitmap* Create(SkPDFCanon*, - const SkBitmap&, - const SkIRect& subset); + static SkPDFBitmap* Create(SkPDFCanon*, const SkBitmap&); ~SkPDFBitmap(); void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; void addResources(SkTSet<SkPDFObject*>* resourceSet, @@ -43,7 +40,7 @@ private: const SkBitmap fBitmap; const SkAutoTUnref<SkPDFObject> fSMask; SkPDFBitmap(const SkBitmap&, SkPDFObject*); - void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; + void emitDict(SkWStream*, SkPDFCatalog*, size_t) const; }; #endif // SkPDFBitmap_DEFINED diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 273b958a8c..a0ad1343af 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -17,10 +17,10 @@ #include "SkPaint.h" #include "SkPath.h" #include "SkPathOps.h" +#include "SkPDFBitmap.h" #include "SkPDFFont.h" #include "SkPDFFormXObject.h" #include "SkPDFGraphicState.h" -#include "SkPDFImage.h" #include "SkPDFResourceDict.h" #include "SkPDFShader.h" #include "SkPDFStream.h" @@ -2126,7 +2126,7 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, if (content.needShape()) { SkPath shape; shape.addRect(SkRect::MakeWH(SkIntToScalar(subset.width()), - SkIntToScalar( subset.height()))); + SkIntToScalar(subset.height()))); shape.transform(matrix); content.setShape(shape); } @@ -2134,8 +2134,12 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, return; } - SkAutoTUnref<SkPDFObject> image( - SkPDFCreateImageObject(fCanon, *bitmap, subset)); + SkBitmap subsetBitmap; + // Should extractSubset be done by the SkPDFDevice? + if (!bitmap->extractSubset(&subsetBitmap, subset)) { + return; + } + SkAutoTUnref<SkPDFObject> image(SkPDFBitmap::Create(fCanon, subsetBitmap)); if (!image) { return; } diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp deleted file mode 100644 index e3971aa57a..0000000000 --- a/src/pdf/SkPDFImage.cpp +++ /dev/null @@ -1,727 +0,0 @@ -/* - * Copyright 2010 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkPDFImage.h" - -#include "SkBitmap.h" -#include "SkColor.h" -#include "SkColorPriv.h" -#include "SkData.h" -#include "SkFlate.h" -#include "SkPDFBitmap.h" -#include "SkPDFCatalog.h" -#include "SkPixelRef.h" -#include "SkRect.h" -#include "SkStream.h" -#include "SkString.h" -#include "SkUnPreMultiply.h" - -static size_t get_uncompressed_size(const SkBitmap& bitmap, - const SkIRect& srcRect) { - switch (bitmap.colorType()) { - case kIndex_8_SkColorType: - return srcRect.width() * srcRect.height(); - case kARGB_4444_SkColorType: - return ((srcRect.width() * 3 + 1) / 2) * srcRect.height(); - case kRGB_565_SkColorType: - return srcRect.width() * 3 * srcRect.height(); - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - case kGray_8_SkColorType: - return srcRect.width() * 3 * srcRect.height(); - case kAlpha_8_SkColorType: - return 1; - default: - SkASSERT(false); - return 0; - } -} - -static SkStream* extract_index8_image(const SkBitmap& bitmap, - const SkIRect& srcRect) { - const int rowBytes = srcRect.width(); - SkStream* stream = SkNEW_ARGS(SkMemoryStream, - (get_uncompressed_size(bitmap, srcRect))); - uint8_t* dst = (uint8_t*)stream->getMemoryBase(); - - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); - dst += rowBytes; - } - return stream; -} - -static SkStream* extract_argb4444_data(const SkBitmap& bitmap, - const SkIRect& srcRect, - bool extractAlpha, - bool* isOpaque, - bool* isTransparent) { - SkStream* stream; - uint8_t* dst = NULL; - if (extractAlpha) { - const int alphaRowBytes = (srcRect.width() + 1) / 2; - stream = SkNEW_ARGS(SkMemoryStream, - (alphaRowBytes * srcRect.height())); - } else { - stream = SkNEW_ARGS(SkMemoryStream, - (get_uncompressed_size(bitmap, srcRect))); - } - dst = (uint8_t*)stream->getMemoryBase(); - - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint16_t* src = bitmap.getAddr16(0, y); - int x; - for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { - if (extractAlpha) { - dst[0] = (SkGetPackedA4444(src[x]) << 4) | - SkGetPackedA4444(src[x + 1]); - *isOpaque &= dst[0] == SK_AlphaOPAQUE; - *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; - dst++; - } else { - dst[0] = (SkGetPackedR4444(src[x]) << 4) | - SkGetPackedG4444(src[x]); - dst[1] = (SkGetPackedB4444(src[x]) << 4) | - SkGetPackedR4444(src[x + 1]); - dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | - SkGetPackedB4444(src[x + 1]); - dst += 3; - } - } - if (srcRect.width() & 1) { - if (extractAlpha) { - dst[0] = (SkGetPackedA4444(src[x]) << 4); - *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0); - *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0); - dst++; - - } else { - dst[0] = (SkGetPackedR4444(src[x]) << 4) | - SkGetPackedG4444(src[x]); - dst[1] = (SkGetPackedB4444(src[x]) << 4); - dst += 2; - } - } - } - return stream; -} - -static SkStream* extract_rgb565_image(const SkBitmap& bitmap, - const SkIRect& srcRect) { - SkStream* stream = SkNEW_ARGS(SkMemoryStream, - (get_uncompressed_size(bitmap, - srcRect))); - uint8_t* dst = (uint8_t*)stream->getMemoryBase(); - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint16_t* src = bitmap.getAddr16(0, y); - for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { - dst[0] = SkGetPackedR16(src[x]); - dst[1] = SkGetPackedG16(src[x]); - dst[2] = SkGetPackedB16(src[x]); - dst += 3; - } - } - return stream; -} - -static SkStream* extract_gray8_image(const SkBitmap& bitmap, const SkIRect& srcRect) { - SkStream* stream = SkNEW_ARGS(SkMemoryStream, - (get_uncompressed_size(bitmap, srcRect))); - uint8_t* dst = (uint8_t*)stream->getMemoryBase(); - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint8_t* src = bitmap.getAddr8(0, y); - for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { - dst[0] = dst[1] = dst[2] = src[x]; - dst += 3; - } - } - return stream; -} - -static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, - int xOrig, - int yOrig); - -static SkStream* extract_argb8888_data(const SkBitmap& bitmap, - const SkIRect& srcRect, - bool extractAlpha, - bool* isOpaque, - bool* isTransparent) { - size_t streamSize = extractAlpha ? srcRect.width() * srcRect.height() - : get_uncompressed_size(bitmap, srcRect); - SkStream* stream = SkNEW_ARGS(SkMemoryStream, (streamSize)); - uint8_t* dst = (uint8_t*)stream->getMemoryBase(); - - const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable(); - - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint32_t* src = bitmap.getAddr32(0, y); - for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { - SkPMColor c = src[x]; - U8CPU alpha = SkGetPackedA32(c); - if (extractAlpha) { - *isOpaque &= alpha == SK_AlphaOPAQUE; - *isTransparent &= alpha == SK_AlphaTRANSPARENT; - *dst++ = alpha; - } else { - if (SK_AlphaTRANSPARENT == alpha) { - // It is necessary to average the color component of - // transparent pixels with their surrounding neighbors - // since the PDF renderer may separately re-sample the - // alpha and color channels when the image is not - // displayed at its native resolution. Since an alpha of - // zero gives no information about the color component, - // the pathological case is a white image with sharp - // transparency bounds - the color channel goes to black, - // and the should-be-transparent pixels are rendered - // as grey because of the separate soft mask and color - // resizing. - c = get_argb8888_neighbor_avg_color(bitmap, x, y); - *dst++ = SkGetPackedR32(c); - *dst++ = SkGetPackedG32(c); - *dst++ = SkGetPackedB32(c); - } else { - SkUnPreMultiply::Scale s = scaleTable[alpha]; - *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); - *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(c)); - *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); - } - } - } - } - SkASSERT(dst == streamSize + (uint8_t*)stream->getMemoryBase()); - return stream; -} - -static SkStream* extract_a8_alpha(const SkBitmap& bitmap, - const SkIRect& srcRect, - bool* isOpaque, - bool* isTransparent) { - const int alphaRowBytes = srcRect.width(); - SkStream* stream = SkNEW_ARGS(SkMemoryStream, - (alphaRowBytes * srcRect.height())); - uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); - - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint8_t* src = bitmap.getAddr8(0, y); - for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { - alphaDst[0] = src[x]; - *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; - *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; - alphaDst++; - } - } - return stream; -} - -static SkStream* create_black_image() { - SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); - ((uint8_t*)stream->getMemoryBase())[0] = 0; - return stream; -} - -/** - * Extract either the color or image data from a SkBitmap into a SkStream. - * @param bitmap Bitmap to extract data from. - * @param srcRect Region in the bitmap to extract. - * @param extractAlpha Set to true to extract the alpha data or false to - * extract the color data. - * @param isTransparent Pointer to a bool to output whether the alpha is - * completely transparent. May be NULL. Only valid when - * extractAlpha == true. - * @return Unencoded image data, or NULL if either data was not - * available or alpha data was requested but the image was - * entirely transparent or opaque. - */ -static SkStream* extract_image_data(const SkBitmap& bitmap, - const SkIRect& srcRect, - bool extractAlpha, bool* isTransparent) { - SkColorType colorType = bitmap.colorType(); - if (extractAlpha && (kIndex_8_SkColorType == colorType || - kRGB_565_SkColorType == colorType || - kGray_8_SkColorType == colorType)) { - if (isTransparent != NULL) { - *isTransparent = false; - } - return NULL; - } - - SkAutoLockPixels lock(bitmap); - if (NULL == bitmap.getPixels()) { - return NULL; - } - - bool isOpaque = true; - bool transparent = extractAlpha; - SkAutoTDelete<SkStream> stream; - - switch (colorType) { - case kIndex_8_SkColorType: - if (!extractAlpha) { - stream.reset(extract_index8_image(bitmap, srcRect)); - } - break; - case kARGB_4444_SkColorType: - stream.reset(extract_argb4444_data(bitmap, srcRect, extractAlpha, - &isOpaque, &transparent)); - break; - case kRGB_565_SkColorType: - if (!extractAlpha) { - stream.reset(extract_rgb565_image(bitmap, srcRect)); - } - break; - case kGray_8_SkColorType: - if (!extractAlpha) { - stream.reset(extract_gray8_image(bitmap, srcRect)); - } - break; - case kN32_SkColorType: - stream.reset(extract_argb8888_data(bitmap, srcRect, extractAlpha, - &isOpaque, &transparent)); - break; - case kAlpha_8_SkColorType: - if (!extractAlpha) { - stream.reset(create_black_image()); - } else { - stream.reset(extract_a8_alpha(bitmap, srcRect, - &isOpaque, &transparent)); - } - break; - default: - SkASSERT(false); - } - - if (isTransparent != NULL) { - *isTransparent = transparent; - } - if (extractAlpha && (transparent || isOpaque)) { - return NULL; - } - return stream.detach(); -} - -static SkPDFArray* make_indexed_color_space(SkColorTable* table) { - SkPDFArray* result = new SkPDFArray(); - result->reserve(4); - result->appendName("Indexed"); - result->appendName("DeviceRGB"); - result->appendInt(table->count() - 1); - - // Potentially, this could be represented in fewer bytes with a stream. - // Max size as a string is 1.5k. - SkString index; - for (int i = 0; i < table->count(); i++) { - char buf[3]; - SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); - buf[0] = SkGetPackedR32(color); - buf[1] = SkGetPackedG32(color); - buf[2] = SkGetPackedB32(color); - index.append(buf, 3); - } - result->append(new SkPDFString(index))->unref(); - return result; -} - -/** - * Removes the alpha component of an ARGB color (including unpremultiply) while - * keeping the output in the same format as the input. - */ -static uint32_t remove_alpha_argb8888(uint32_t pmColor) { - SkColor color = SkUnPreMultiply::PMColorToColor(pmColor); - return SkPackARGB32NoCheck(SK_AlphaOPAQUE, - SkColorGetR(color), - SkColorGetG(color), - SkColorGetB(color)); -} - -static uint16_t remove_alpha_argb4444(uint16_t pmColor) { - return SkPixel32ToPixel4444( - remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor))); -} - -static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, - int xOrig, int yOrig) { - uint8_t count = 0; - uint16_t r = 0; - uint16_t g = 0; - uint16_t b = 0; - - for (int y = yOrig - 1; y <= yOrig + 1; y++) { - if (y < 0 || y >= bitmap.height()) { - continue; - } - uint32_t* src = bitmap.getAddr32(0, y); - for (int x = xOrig - 1; x <= xOrig + 1; x++) { - if (x < 0 || x >= bitmap.width()) { - continue; - } - if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) { - uint32_t color = remove_alpha_argb8888(src[x]); - r += SkGetPackedR32(color); - g += SkGetPackedG32(color); - b += SkGetPackedB32(color); - count++; - } - } - } - - if (count == 0) { - return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); - } else { - return SkPackARGB32NoCheck(SK_AlphaOPAQUE, - r / count, g / count, b / count); - } -} - -static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap, - int xOrig, int yOrig) { - uint8_t count = 0; - uint8_t r = 0; - uint8_t g = 0; - uint8_t b = 0; - - for (int y = yOrig - 1; y <= yOrig + 1; y++) { - if (y < 0 || y >= bitmap.height()) { - continue; - } - uint16_t* src = bitmap.getAddr16(0, y); - for (int x = xOrig - 1; x <= xOrig + 1; x++) { - if (x < 0 || x >= bitmap.width()) { - continue; - } - if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) { - uint16_t color = remove_alpha_argb4444(src[x]); - r += SkGetPackedR4444(color); - g += SkGetPackedG4444(color); - b += SkGetPackedB4444(color); - count++; - } - } - } - - if (count == 0) { - return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0); - } else { - return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, - r / count, g / count, b / count); - } -} - -static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap, - const SkIRect& srcRect) { - SkBitmap outBitmap; - outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height())); - int dstRow = 0; - - SkAutoLockPixels outBitmapPixelLock(outBitmap); - SkAutoLockPixels bitmapPixelLock(bitmap); - switch (bitmap.colorType()) { - case kARGB_4444_SkColorType: { - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint16_t* dst = outBitmap.getAddr16(0, dstRow); - uint16_t* src = bitmap.getAddr16(0, y); - for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { - uint8_t a = SkGetPackedA4444(src[x]); - // It is necessary to average the color component of - // transparent pixels with their surrounding neighbors - // since the PDF renderer may separately re-sample the - // alpha and color channels when the image is not - // displayed at its native resolution. Since an alpha of - // zero gives no information about the color component, - // the pathological case is a white image with sharp - // transparency bounds - the color channel goes to black, - // and the should-be-transparent pixels are rendered - // as grey because of the separate soft mask and color - // resizing. - if (a == (SK_AlphaTRANSPARENT & 0x0F)) { - *dst = get_argb4444_neighbor_avg_color(bitmap, x, y); - } else { - *dst = remove_alpha_argb4444(src[x]); - } - dst++; - } - dstRow++; - } - break; - } - case kN32_SkColorType: { - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint32_t* dst = outBitmap.getAddr32(0, dstRow); - uint32_t* src = bitmap.getAddr32(0, y); - for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { - uint8_t a = SkGetPackedA32(src[x]); - if (a == SK_AlphaTRANSPARENT) { - *dst = get_argb8888_neighbor_avg_color(bitmap, x, y); - } else { - *dst = remove_alpha_argb8888(src[x]); - } - dst++; - } - dstRow++; - } - break; - } - default: - SkASSERT(false); - } - - outBitmap.setImmutable(); - - return outBitmap; -} - -// static -SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, - const SkIRect& srcRect) { - if (bitmap.colorType() == kUnknown_SkColorType) { - return NULL; - } - - bool isTransparent = false; - SkAutoTDelete<SkStream> alphaData; - if (!bitmap.isOpaque()) { - // Note that isOpaque is not guaranteed to return false for bitmaps - // with alpha support but a completely opaque alpha channel, - // so alphaData may still be NULL if we have a completely opaque - // (or transparent) bitmap. - alphaData.reset( - extract_image_data(bitmap, srcRect, true, &isTransparent)); - } - if (isTransparent) { - return NULL; - } - - SkPDFImage* image; - SkColorType colorType = bitmap.colorType(); - if (alphaData.get() != NULL && (kN32_SkColorType == colorType || - kARGB_4444_SkColorType == colorType)) { - if (kN32_SkColorType == colorType) { - image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, - SkIRect::MakeWH(srcRect.width(), - srcRect.height()))); - } else { - SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect); - image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false, - SkIRect::MakeWH(srcRect.width(), - srcRect.height()))); - } - } else { - image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect)); - } - if (alphaData.get() != NULL) { - SkAutoTUnref<SkPDFImage> mask( - SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, true, srcRect))); - image->insert("SMask", new SkPDFObjRef(mask))->unref(); - } - return image; -} - -SkPDFImage::~SkPDFImage() {} - -SkPDFImage::SkPDFImage(SkStream* stream, - const SkBitmap& bitmap, - bool isAlpha, - const SkIRect& srcRect) - : fIsAlpha(isAlpha), - fSrcRect(srcRect) { - - if (bitmap.isImmutable()) { - fBitmap = bitmap; - } else { - bitmap.deepCopyTo(&fBitmap); - fBitmap.setImmutable(); - } - - if (stream != NULL) { - this->setData(stream); - fStreamValid = true; - } else { - fStreamValid = false; - } - - SkColorType colorType = fBitmap.colorType(); - - insertName("Type", "XObject"); - insertName("Subtype", "Image"); - - bool alphaOnly = (kAlpha_8_SkColorType == colorType); - - if (!isAlpha && alphaOnly) { - // For alpha only images, we stretch a single pixel of black for - // the color/shape part. - SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); - insert("Width", one.get()); - insert("Height", one.get()); - } else { - insertInt("Width", fSrcRect.width()); - insertInt("Height", fSrcRect.height()); - } - - if (isAlpha || alphaOnly) { - insertName("ColorSpace", "DeviceGray"); - } else if (kIndex_8_SkColorType == colorType) { - SkAutoLockPixels alp(fBitmap); - insert("ColorSpace", - make_indexed_color_space(fBitmap.getColorTable()))->unref(); - } else { - insertName("ColorSpace", "DeviceRGB"); - } - - int bitsPerComp = 8; - if (kARGB_4444_SkColorType == colorType) { - bitsPerComp = 4; - } - insertInt("BitsPerComponent", bitsPerComp); - - if (kRGB_565_SkColorType == colorType) { - SkASSERT(!isAlpha); - SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); - SkAutoTUnref<SkPDFScalar> scale5Val( - new SkPDFScalar(8.2258f)); // 255/2^5-1 - SkAutoTUnref<SkPDFScalar> scale6Val( - new SkPDFScalar(4.0476f)); // 255/2^6-1 - SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); - decodeValue->reserve(6); - decodeValue->append(zeroVal.get()); - decodeValue->append(scale5Val.get()); - decodeValue->append(zeroVal.get()); - decodeValue->append(scale6Val.get()); - decodeValue->append(zeroVal.get()); - decodeValue->append(scale5Val.get()); - insert("Decode", decodeValue.get()); - } -} - -SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) - : SkPDFStream(pdfImage), - fBitmap(pdfImage.fBitmap), - fIsAlpha(pdfImage.fIsAlpha), - fSrcRect(pdfImage.fSrcRect), - fStreamValid(pdfImage.fStreamValid) { - // Nothing to do here - the image params are already copied in SkPDFStream's - // constructor, and the bitmap will be regenerated and encoded in - // populate. -} - -bool SkPDFImage::populate(SkPDFCatalog* catalog) { - if (getState() == kUnused_State) { - // Initializing image data for the first time. - // Fallback method - if (!fStreamValid) { - SkAutoTDelete<SkStream> stream( - extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); - this->setData(stream); - fStreamValid = true; - } - return INHERITED::populate(catalog); - } -#ifndef SK_NO_FLATE - else if (getState() == kNoCompression_State) { - // Compression has not been requested when the stream was first created, - // but the new catalog wants it compressed. - if (!getSubstitute()) { - SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); - setSubstitute(substitute); - catalog->setSubstitute(this, substitute); - } - return false; - } -#endif // SK_NO_FLATE - return true; -} - -#if 0 // reenable when we can figure out the JPEG colorspace -namespace { -/** - * This PDFObject assumes that its constructor was handed - * Jpeg-encoded data that can be directly embedded into a PDF. - */ -class PDFJPEGImage : public SkPDFObject { - SkAutoTUnref<SkData> fData; - int fWidth; - int fHeight; -public: - PDFJPEGImage(SkData* data, int width, int height) - : fData(SkRef(data)), fWidth(width), fHeight(height) {} - virtual void emitObject( - SkWStream* stream, - SkPDFCatalog* catalog, bool indirect) SK_OVERRIDE { - if (indirect) { - this->emitIndirectObject(stream, catalog); - return; - } - SkASSERT(fData.get()); - const char kPrefaceFormat[] = - "<<" - "/Type /XObject\n" - "/Subtype /Image\n" - "/Width %d\n" - "/Height %d\n" - "/ColorSpace /DeviceRGB\n" // or DeviceGray - "/BitsPerComponent 8\n" - "/Filter /DCTDecode\n" - "/ColorTransform 0\n" - "/Length " SK_SIZE_T_SPECIFIER "\n" - ">> stream\n"; - SkString preface( - SkStringPrintf(kPrefaceFormat, fWidth, fHeight, fData->size())); - const char kPostface[] = "\nendstream"; - stream->write(preface.c_str(), preface.size()); - stream->write(fData->data(), fData->size()); - stream->write(kPostface, sizeof(kPostface)); - } -}; - -/** - * If the bitmap is not subsetted, return its encoded data, if - * availible. - */ -static inline SkData* ref_encoded_data(const SkBitmap& bm) { - if ((NULL == bm.pixelRef()) - || !bm.pixelRefOrigin().isZero() - || (bm.info().dimensions() != bm.pixelRef()->info().dimensions())) { - return NULL; - } - return bm.pixelRef()->refEncodedData(); -} - -/* - * This functions may give false negatives but no false positives. - */ -static bool is_jfif_jpeg(SkData* data) { - if (!data || (data->size() < 11)) { - return false; - } - const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0}; - const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0}; - // 0 1 2 3 4 5 6 7 8 9 10 - // FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ... - return ((0 == memcmp(data->bytes(), bytesZeroToThree, - sizeof(bytesZeroToThree))) - && (0 == memcmp(data->bytes() + 6, bytesSixToTen, - sizeof(bytesSixToTen)))); -} -} // namespace -#endif - -SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon, - const SkBitmap& bitmap, - const SkIRect& subset) { - if (SkPDFObject* pdfBitmap = SkPDFBitmap::Create(canon, bitmap, subset)) { - return pdfBitmap; - } -#if 0 // reenable when we can figure out the JPEG colorspace - if (SkIRect::MakeWH(bitmap.width(), bitmap.height()) == subset) { - SkAutoTUnref<SkData> encodedData(ref_encoded_data(bitmap)); - if (is_jfif_jpeg(encodedData)) { - return SkNEW_ARGS(PDFJPEGImage, - (encodedData, bitmap.width(), bitmap.height())); - } - } -#endif - return SkPDFImage::CreateImage(bitmap, subset); -} diff --git a/src/pdf/SkPDFImage.h b/src/pdf/SkPDFImage.h deleted file mode 100644 index 64be971bfc..0000000000 --- a/src/pdf/SkPDFImage.h +++ /dev/null @@ -1,91 +0,0 @@ - -/* - * Copyright 2010 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkPDFImage_DEFINED -#define SkPDFImage_DEFINED - -#include "SkPicture.h" -#include "SkPDFDevice.h" -#include "SkPDFStream.h" -#include "SkPDFTypes.h" -#include "SkRefCnt.h" - -class SkBitmap; -class SkData; -class SkPDFCatalog; -struct SkIRect; - -/** - * Return the mose efficient availible encoding of the given bitmap. - */ -SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon, - const SkBitmap&, - const SkIRect& subset); - -/** \class SkPDFImage - - An image XObject. -*/ - -// We could play the same trick here as is done in SkPDFGraphicState, storing -// a copy of the Bitmap object (not the pixels), the pixel generation number, -// and settings used from the paint to canonicalize image objects. -class SkPDFImage : public SkPDFStream { -public: - /** Create a new Image XObject to represent the passed bitmap. - * @param bitmap The image to encode. - * @param srcRect The rectangle to cut out of bitmap. - * @param paint Used to calculate alpha, masks, etc. - * @return The image XObject or NUll if there is nothing to draw for - * the given parameters. - */ - static SkPDFImage* CreateImage(const SkBitmap& bitmap, - const SkIRect& srcRect); - - virtual ~SkPDFImage(); - - bool isEmpty() { - return fSrcRect.isEmpty(); - } - -private: - SkBitmap fBitmap; - bool fIsAlpha; - SkIRect fSrcRect; - bool fStreamValid; - - /** Create a PDF image XObject. Entries for the image properties are - * automatically added to the stream dictionary. - * @param stream The image stream. May be NULL. Otherwise, this - * (instead of the input bitmap) will be used as the - * PDF's content stream, possibly with lossless encoding. - * Will be duplicated, and left in indeterminate state. - * @param bitmap The image. If a stream is not given, its color data - * will be used as the image. If a stream is given, this - * is used for configuration only. - * @param isAlpha Whether or not this is the alpha of an image. - * @param srcRect The clipping applied to bitmap before generating - * imageData. - */ - SkPDFImage(SkStream* stream, const SkBitmap& bitmap, bool isAlpha, - const SkIRect& srcRect); - - /** Copy constructor, used to generate substitutes. - * @param image The SkPDFImage to copy. - */ - SkPDFImage(SkPDFImage& pdfImage); - - // Populate the stream dictionary. This method returns false if - // fSubstitute should be used. - virtual bool populate(SkPDFCatalog* catalog); - - typedef SkPDFStream INHERITED; -}; - -#endif |