| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkColorPriv.h" | 8 #include "SkColorPriv.h" |
| 9 #include "SkData.h" | 9 #include "SkData.h" |
| 10 #include "SkDeflate.h" | 10 #include "SkDeflate.h" |
| 11 #include "SkImageGenerator.h" | 11 #include "SkImage_Base.h" |
| 12 #include "SkJpegInfo.h" | 12 #include "SkJpegInfo.h" |
| 13 #include "SkPDFBitmap.h" | 13 #include "SkPDFBitmap.h" |
| 14 #include "SkPDFCanon.h" | 14 #include "SkPDFCanon.h" |
| 15 #include "SkPixelRef.h" | |
| 16 #include "SkStream.h" | 15 #include "SkStream.h" |
| 17 #include "SkUnPreMultiply.h" | 16 #include "SkUnPreMultiply.h" |
| 18 | 17 |
| 19 //////////////////////////////////////////////////////////////////////////////// | 18 //////////////////////////////////////////////////////////////////////////////// |
| 20 | 19 |
| 21 static void pdf_stream_begin(SkWStream* stream) { | 20 static void pdf_stream_begin(SkWStream* stream) { |
| 22 static const char streamBegin[] = " stream\n"; | 21 static const char streamBegin[] = " stream\n"; |
| 23 stream->write(streamBegin, strlen(streamBegin)); | 22 stream->write(streamBegin, strlen(streamBegin)); |
| 24 } | 23 } |
| 25 | 24 |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 SkDEBUGFAIL("unexpected color type"); | 237 SkDEBUGFAIL("unexpected color type"); |
| 239 } | 238 } |
| 240 } | 239 } |
| 241 | 240 |
| 242 //////////////////////////////////////////////////////////////////////////////// | 241 //////////////////////////////////////////////////////////////////////////////// |
| 243 | 242 |
| 244 namespace { | 243 namespace { |
| 245 // This SkPDFObject only outputs the alpha layer of the given bitmap. | 244 // This SkPDFObject only outputs the alpha layer of the given bitmap. |
| 246 class PDFAlphaBitmap : public SkPDFObject { | 245 class PDFAlphaBitmap : public SkPDFObject { |
| 247 public: | 246 public: |
| 248 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} | 247 PDFAlphaBitmap(const SkImage* image) : fImage(SkRef(image)) {} |
| 249 ~PDFAlphaBitmap() {} | 248 ~PDFAlphaBitmap() {} |
| 250 void emitObject(SkWStream*, | 249 void emitObject(SkWStream*, |
| 251 const SkPDFObjNumMap&, | 250 const SkPDFObjNumMap&, |
| 252 const SkPDFSubstituteMap&) const override; | 251 const SkPDFSubstituteMap&) const override; |
| 253 | 252 |
| 254 private: | 253 private: |
| 255 const SkBitmap fBitmap; | 254 SkAutoTUnref<const SkImage> fImage; |
| 256 }; | 255 }; |
| 257 | 256 |
| 258 void PDFAlphaBitmap::emitObject(SkWStream* stream, | 257 void PDFAlphaBitmap::emitObject(SkWStream* stream, |
| 259 const SkPDFObjNumMap& objNumMap, | 258 const SkPDFObjNumMap& objNumMap, |
| 260 const SkPDFSubstituteMap& substitutes) const { | 259 const SkPDFSubstituteMap& substitutes) const { |
| 261 SkAutoLockPixels autoLockPixels(fBitmap); | 260 // Should we lock as a SkPixMap, not a SkBitmap? |
| 262 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || | 261 SkBitmap bitmap; |
| 263 fBitmap.getColorTable()); | 262 SkISize size = fImage->dimensions(); |
| 263 if (!as_IB(fImage.get())->getROPixels(&bitmap) || |
| 264 bitmap.dimensions() != size) { |
| 265 bitmap.reset(); // bitmap_alpha_to_a8 will fill with zeroes. |
| 266 } |
| 267 SkAutoLockPixels autoLockPixels(bitmap); |
| 268 SkASSERT(bitmap.colorType() != kIndex_8_SkColorType || |
| 269 bitmap.getColorTable()); |
| 264 | 270 |
| 265 // Write to a temporary buffer to get the compressed length. | 271 // Write to a temporary buffer to get the compressed length. |
| 266 SkDynamicMemoryWStream buffer; | 272 SkDynamicMemoryWStream buffer; |
| 267 SkDeflateWStream deflateWStream(&buffer); | 273 SkDeflateWStream deflateWStream(&buffer); |
| 268 bitmap_alpha_to_a8(fBitmap, &deflateWStream); | 274 bitmap_alpha_to_a8(bitmap, &deflateWStream); |
| 269 deflateWStream.finalize(); // call before detachAsStream(). | 275 deflateWStream.finalize(); // call before detachAsStream(). |
| 270 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 276 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| 271 | 277 |
| 272 SkPDFDict pdfDict("XObject"); | 278 SkPDFDict pdfDict("XObject"); |
| 273 pdfDict.insertName("Subtype", "Image"); | 279 pdfDict.insertName("Subtype", "Image"); |
| 274 pdfDict.insertInt("Width", fBitmap.width()); | 280 pdfDict.insertInt("Width", size.width()); |
| 275 pdfDict.insertInt("Height", fBitmap.height()); | 281 pdfDict.insertInt("Height", size.height()); |
| 276 pdfDict.insertName("ColorSpace", "DeviceGray"); | 282 pdfDict.insertName("ColorSpace", "DeviceGray"); |
| 277 pdfDict.insertInt("BitsPerComponent", 8); | 283 pdfDict.insertInt("BitsPerComponent", 8); |
| 278 pdfDict.insertName("Filter", "FlateDecode"); | 284 pdfDict.insertName("Filter", "FlateDecode"); |
| 279 pdfDict.insertInt("Length", asset->getLength()); | 285 pdfDict.insertInt("Length", asset->getLength()); |
| 280 pdfDict.emitObject(stream, objNumMap, substitutes); | 286 pdfDict.emitObject(stream, objNumMap, substitutes); |
| 281 | 287 |
| 282 pdf_stream_begin(stream); | 288 pdf_stream_begin(stream); |
| 283 stream->writeStream(asset.get(), asset->getLength()); | 289 stream->writeStream(asset.get(), asset->getLength()); |
| 284 pdf_stream_end(stream); | 290 pdf_stream_end(stream); |
| 285 } | 291 } |
| 286 } // namespace | 292 } // namespace |
| 287 | 293 |
| 288 //////////////////////////////////////////////////////////////////////////////// | 294 //////////////////////////////////////////////////////////////////////////////// |
| 289 | 295 |
| 290 namespace { | 296 namespace { |
| 291 class PDFDefaultBitmap : public SkPDFBitmap { | 297 class PDFDefaultBitmap : public SkPDFObject { |
| 292 public: | 298 public: |
| 293 const SkAutoTUnref<SkPDFObject> fSMask; | |
| 294 void emitObject(SkWStream*, | 299 void emitObject(SkWStream*, |
| 295 const SkPDFObjNumMap&, | 300 const SkPDFObjNumMap&, |
| 296 const SkPDFSubstituteMap&) const override; | 301 const SkPDFSubstituteMap&) const override; |
| 297 void addResources(SkPDFObjNumMap*, | 302 void addResources(SkPDFObjNumMap*, |
| 298 const SkPDFSubstituteMap&) const override; | 303 const SkPDFSubstituteMap&) const override; |
| 299 PDFDefaultBitmap(const SkBitmap& bm, SkPDFObject* smask) | 304 PDFDefaultBitmap(const SkImage* image, SkPDFObject* smask) |
| 300 : SkPDFBitmap(bm), fSMask(smask) {} | 305 : fImage(SkRef(image)), fSMask(smask) {} |
| 306 |
| 307 private: |
| 308 SkAutoTUnref<const SkImage> fImage; |
| 309 const SkAutoTUnref<SkPDFObject> fSMask; |
| 301 }; | 310 }; |
| 302 } // namespace | 311 } // namespace |
| 303 | 312 |
| 304 void PDFDefaultBitmap::addResources( | 313 void PDFDefaultBitmap::addResources( |
| 305 SkPDFObjNumMap* catalog, | 314 SkPDFObjNumMap* catalog, |
| 306 const SkPDFSubstituteMap& substitutes) const { | 315 const SkPDFSubstituteMap& substitutes) const { |
| 307 if (fSMask.get()) { | 316 if (fSMask.get()) { |
| 308 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); | 317 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); |
| 309 SkASSERT(obj); | 318 SkASSERT(obj); |
| 310 if (catalog->addObject(obj)) { | 319 if (catalog->addObject(obj)) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 339 tablePtr += 3; | 348 tablePtr += 3; |
| 340 } | 349 } |
| 341 SkString tableString(tableArray, 3 * table->count()); | 350 SkString tableString(tableArray, 3 * table->count()); |
| 342 result->appendString(tableString); | 351 result->appendString(tableString); |
| 343 return result; | 352 return result; |
| 344 } | 353 } |
| 345 | 354 |
| 346 void PDFDefaultBitmap::emitObject(SkWStream* stream, | 355 void PDFDefaultBitmap::emitObject(SkWStream* stream, |
| 347 const SkPDFObjNumMap& objNumMap, | 356 const SkPDFObjNumMap& objNumMap, |
| 348 const SkPDFSubstituteMap& substitutes) const { | 357 const SkPDFSubstituteMap& substitutes) const { |
| 349 SkAutoLockPixels autoLockPixels(fBitmap); | 358 SkBitmap bitmap; // Should we lock as a SkPixMap, not a SkBitmap? |
| 350 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || | 359 SkISize size = fImage->dimensions(); |
| 351 fBitmap.getColorTable()); | 360 if (!as_IB(fImage.get())->getROPixels(&bitmap) || |
| 361 bitmap.dimensions() != size) { |
| 362 bitmap.reset(); // bitmap_to_pdf_pixels will fill with zeroes. |
| 363 } |
| 364 SkAutoLockPixels autoLockPixels(bitmap); // TODO(halcanry): test |
| 365 // with malformed images? |
| 366 SkASSERT(bitmap.colorType() != kIndex_8_SkColorType || |
| 367 bitmap.getColorTable()); |
| 352 | 368 |
| 353 // Write to a temporary buffer to get the compressed length. | 369 // Write to a temporary buffer to get the compressed length. |
| 354 SkDynamicMemoryWStream buffer; | 370 SkDynamicMemoryWStream buffer; |
| 355 SkDeflateWStream deflateWStream(&buffer); | 371 SkDeflateWStream deflateWStream(&buffer); |
| 356 bitmap_to_pdf_pixels(fBitmap, &deflateWStream); | 372 bitmap_to_pdf_pixels(bitmap, &deflateWStream); |
| 357 deflateWStream.finalize(); // call before detachAsStream(). | 373 deflateWStream.finalize(); // call before detachAsStream(). |
| 358 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 374 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| 359 | 375 |
| 360 SkPDFDict pdfDict("XObject"); | 376 SkPDFDict pdfDict("XObject"); |
| 361 pdfDict.insertName("Subtype", "Image"); | 377 pdfDict.insertName("Subtype", "Image"); |
| 362 pdfDict.insertInt("Width", fBitmap.width()); | 378 pdfDict.insertInt("Width", bitmap.width()); |
| 363 pdfDict.insertInt("Height", fBitmap.height()); | 379 pdfDict.insertInt("Height", bitmap.height()); |
| 364 if (fBitmap.colorType() == kIndex_8_SkColorType) { | 380 if (bitmap.colorType() == kIndex_8_SkColorType) { |
| 365 SkASSERT(1 == pdf_color_component_count(fBitmap.colorType())); | 381 SkASSERT(1 == pdf_color_component_count(bitmap.colorType())); |
| 366 pdfDict.insertObject("ColorSpace", | 382 pdfDict.insertObject("ColorSpace", |
| 367 make_indexed_color_space(fBitmap.getColorTable())); | 383 make_indexed_color_space(bitmap.getColorTable())); |
| 368 } else if (1 == pdf_color_component_count(fBitmap.colorType())) { | 384 } else if (1 == pdf_color_component_count(bitmap.colorType())) { |
| 369 pdfDict.insertName("ColorSpace", "DeviceGray"); | 385 pdfDict.insertName("ColorSpace", "DeviceGray"); |
| 370 } else { | 386 } else { |
| 371 pdfDict.insertName("ColorSpace", "DeviceRGB"); | 387 pdfDict.insertName("ColorSpace", "DeviceRGB"); |
| 372 } | 388 } |
| 373 pdfDict.insertInt("BitsPerComponent", 8); | 389 pdfDict.insertInt("BitsPerComponent", 8); |
| 374 if (fSMask) { | 390 if (fSMask) { |
| 375 pdfDict.insertObjRef("SMask", SkRef(fSMask.get())); | 391 pdfDict.insertObjRef("SMask", SkRef(fSMask.get())); |
| 376 } | 392 } |
| 377 pdfDict.insertName("Filter", "FlateDecode"); | 393 pdfDict.insertName("Filter", "FlateDecode"); |
| 378 pdfDict.insertInt("Length", asset->getLength()); | 394 pdfDict.insertInt("Length", asset->getLength()); |
| 379 pdfDict.emitObject(stream, objNumMap, substitutes); | 395 pdfDict.emitObject(stream, objNumMap, substitutes); |
| 380 | 396 |
| 381 pdf_stream_begin(stream); | 397 pdf_stream_begin(stream); |
| 382 stream->writeStream(asset.get(), asset->getLength()); | 398 stream->writeStream(asset.get(), asset->getLength()); |
| 383 pdf_stream_end(stream); | 399 pdf_stream_end(stream); |
| 384 } | 400 } |
| 385 | 401 |
| 386 //////////////////////////////////////////////////////////////////////////////// | 402 //////////////////////////////////////////////////////////////////////////////// |
| 387 | 403 |
| 388 static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) { | |
| 389 if (bm.isImmutable()) { | |
| 390 return bm; | |
| 391 } | |
| 392 bm.copyTo(copy); | |
| 393 copy->setImmutable(); | |
| 394 return *copy; | |
| 395 } | |
| 396 | |
| 397 namespace { | 404 namespace { |
| 398 /** | 405 /** |
| 399 * This PDFObject assumes that its constructor was handed YUV JFIF | 406 * This PDFObject assumes that its constructor was handed YUV or |
| 400 * Jpeg-encoded data that can be directly embedded into a PDF. | 407 * Grayscale JFIF Jpeg-encoded data that can be directly embedded |
| 408 * into a PDF. |
| 401 */ | 409 */ |
| 402 class PDFJpegBitmap : public SkPDFBitmap { | 410 class PDFJpegBitmap : public SkPDFObject { |
| 403 public: | 411 public: |
| 412 SkISize fSize; |
| 404 SkAutoTUnref<SkData> fData; | 413 SkAutoTUnref<SkData> fData; |
| 405 bool fIsYUV; | 414 bool fIsYUV; |
| 406 PDFJpegBitmap(const SkBitmap& bm, SkData* data, bool isYUV) | 415 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV) |
| 407 : SkPDFBitmap(bm), fData(SkRef(data)), fIsYUV(isYUV) {} | 416 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) {} |
| 408 void emitObject(SkWStream*, | 417 void emitObject(SkWStream*, |
| 409 const SkPDFObjNumMap&, | 418 const SkPDFObjNumMap&, |
| 410 const SkPDFSubstituteMap&) const override; | 419 const SkPDFSubstituteMap&) const override; |
| 411 }; | 420 }; |
| 412 | 421 |
| 413 void PDFJpegBitmap::emitObject(SkWStream* stream, | 422 void PDFJpegBitmap::emitObject(SkWStream* stream, |
| 414 const SkPDFObjNumMap& objNumMap, | 423 const SkPDFObjNumMap& objNumMap, |
| 415 const SkPDFSubstituteMap& substituteMap) const { | 424 const SkPDFSubstituteMap& substituteMap) const { |
| 416 SkPDFDict pdfDict("XObject"); | 425 SkPDFDict pdfDict("XObject"); |
| 417 pdfDict.insertName("Subtype", "Image"); | 426 pdfDict.insertName("Subtype", "Image"); |
| 418 pdfDict.insertInt("Width", fBitmap.width()); | 427 pdfDict.insertInt("Width", fSize.width()); |
| 419 pdfDict.insertInt("Height", fBitmap.height()); | 428 pdfDict.insertInt("Height", fSize.height()); |
| 420 if (fIsYUV) { | 429 if (fIsYUV) { |
| 421 pdfDict.insertName("ColorSpace", "DeviceRGB"); | 430 pdfDict.insertName("ColorSpace", "DeviceRGB"); |
| 422 } else { | 431 } else { |
| 423 pdfDict.insertName("ColorSpace", "DeviceGray"); | 432 pdfDict.insertName("ColorSpace", "DeviceGray"); |
| 424 } | 433 } |
| 425 pdfDict.insertInt("BitsPerComponent", 8); | 434 pdfDict.insertInt("BitsPerComponent", 8); |
| 426 pdfDict.insertName("Filter", "DCTDecode"); | 435 pdfDict.insertName("Filter", "DCTDecode"); |
| 427 pdfDict.insertInt("ColorTransform", 0); | 436 pdfDict.insertInt("ColorTransform", 0); |
| 428 pdfDict.insertInt("Length", SkToInt(fData->size())); | 437 pdfDict.insertInt("Length", SkToInt(fData->size())); |
| 429 pdfDict.emitObject(stream, objNumMap, substituteMap); | 438 pdfDict.emitObject(stream, objNumMap, substituteMap); |
| 430 pdf_stream_begin(stream); | 439 pdf_stream_begin(stream); |
| 431 stream->write(fData->data(), fData->size()); | 440 stream->write(fData->data(), fData->size()); |
| 432 pdf_stream_end(stream); | 441 pdf_stream_end(stream); |
| 433 } | 442 } |
| 434 } // namespace | 443 } // namespace |
| 435 | 444 |
| 436 //////////////////////////////////////////////////////////////////////////////// | 445 //////////////////////////////////////////////////////////////////////////////// |
| 437 | 446 |
| 438 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) { | 447 SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) { |
| 439 SkASSERT(canon); | 448 SkAutoTUnref<SkData> data(image->refEncoded()); |
| 440 if (!SkColorTypeIsValid(bitmap.colorType()) || | 449 SkJFIFInfo info; |
| 441 kUnknown_SkColorType == bitmap.colorType()) { | 450 if (data && SkIsJFIF(data, &info)) { |
| 442 return nullptr; | 451 bool yuv = info.fType == SkJFIFInfo::kYCbCr; |
| 443 } | 452 if (info.fSize == image->dimensions()) { // Sanity check. |
| 444 SkBitmap copy; | 453 // hold on to data, not image. |
| 445 const SkBitmap& bm = immutable_bitmap(bitmap, ©); | 454 return new PDFJpegBitmap(info.fSize, data, yuv); |
| 446 if (bm.drawsNothing()) { | |
| 447 return nullptr; | |
| 448 } | |
| 449 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) { | |
| 450 return SkRef(canonBitmap); | |
| 451 } | |
| 452 | |
| 453 if (bm.pixelRef() && bm.pixelRefOrigin().isZero() && | |
| 454 bm.dimensions() == bm.pixelRef()->info().dimensions()) { | |
| 455 // Requires the bitmap to be backed by lazy pixels. | |
| 456 SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData()); | |
| 457 SkJFIFInfo info; | |
| 458 if (data && SkIsJFIF(data, &info)) { | |
| 459 bool yuv = info.fType == SkJFIFInfo::kYCbCr; | |
| 460 SkPDFBitmap* pdfBitmap = new PDFJpegBitmap(bm, data, yuv); | |
| 461 canon->addBitmap(pdfBitmap); | |
| 462 return pdfBitmap; | |
| 463 } | 455 } |
| 464 } | 456 } |
| 465 | 457 SkPDFObject* smask = |
| 466 SkPDFObject* smask = nullptr; | 458 image->isOpaque() ? nullptr : new PDFAlphaBitmap(image); |
| 467 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { | 459 return new PDFDefaultBitmap(image, smask); |
| 468 smask = new PDFAlphaBitmap(bm); | |
| 469 } | |
| 470 SkPDFBitmap* pdfBitmap = new PDFDefaultBitmap(bm, smask); | |
| 471 canon->addBitmap(pdfBitmap); | |
| 472 return pdfBitmap; | |
| 473 } | 460 } |
| OLD | NEW |