Chromium Code Reviews| 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 |
| 18 void image_get_ro_pixels(const SkImage* image, SkBitmap* dst) { | |
| 19 if(as_IB(image)->getROPixels(dst) | |
| 20 && dst->dimensions() == image->dimensions()) { | |
| 21 return; | |
| 22 } | |
| 23 // no pixels or wrong size: fill with zeros. | |
| 24 SkAlphaType at = image->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaTy pe; | |
| 25 dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), at)); | |
| 26 } | |
| 27 | |
| 19 //////////////////////////////////////////////////////////////////////////////// | 28 //////////////////////////////////////////////////////////////////////////////// |
| 20 | 29 |
| 21 static void pdf_stream_begin(SkWStream* stream) { | 30 static void pdf_stream_begin(SkWStream* stream) { |
| 22 static const char streamBegin[] = " stream\n"; | 31 static const char streamBegin[] = " stream\n"; |
| 23 stream->write(streamBegin, strlen(streamBegin)); | 32 stream->write(streamBegin, strlen(streamBegin)); |
| 24 } | 33 } |
| 25 | 34 |
| 26 static void pdf_stream_end(SkWStream* stream) { | 35 static void pdf_stream_end(SkWStream* stream) { |
| 27 static const char streamEnd[] = "\nendstream"; | 36 static const char streamEnd[] = "\nendstream"; |
| 28 stream->write(streamEnd, strlen(streamEnd)); | 37 stream->write(streamEnd, strlen(streamEnd)); |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 238 SkDEBUGFAIL("unexpected color type"); | 247 SkDEBUGFAIL("unexpected color type"); |
| 239 } | 248 } |
| 240 } | 249 } |
| 241 | 250 |
| 242 //////////////////////////////////////////////////////////////////////////////// | 251 //////////////////////////////////////////////////////////////////////////////// |
| 243 | 252 |
| 244 namespace { | 253 namespace { |
| 245 // This SkPDFObject only outputs the alpha layer of the given bitmap. | 254 // This SkPDFObject only outputs the alpha layer of the given bitmap. |
| 246 class PDFAlphaBitmap : public SkPDFObject { | 255 class PDFAlphaBitmap : public SkPDFObject { |
| 247 public: | 256 public: |
| 248 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} | 257 PDFAlphaBitmap(const SkImage* image) : fImage(SkRef(image)) {} |
| 249 ~PDFAlphaBitmap() {} | 258 ~PDFAlphaBitmap() {} |
| 250 void emitObject(SkWStream*, | 259 void emitObject(SkWStream*, |
| 251 const SkPDFObjNumMap&, | 260 const SkPDFObjNumMap&, |
| 252 const SkPDFSubstituteMap&) const override; | 261 const SkPDFSubstituteMap&) const override; |
| 253 | 262 |
| 254 private: | 263 private: |
| 255 const SkBitmap fBitmap; | 264 SkAutoTUnref<const SkImage> fImage; |
| 256 }; | 265 }; |
| 257 | 266 |
| 258 void PDFAlphaBitmap::emitObject(SkWStream* stream, | 267 void PDFAlphaBitmap::emitObject(SkWStream* stream, |
| 259 const SkPDFObjNumMap& objNumMap, | 268 const SkPDFObjNumMap& objNumMap, |
| 260 const SkPDFSubstituteMap& substitutes) const { | 269 const SkPDFSubstituteMap& substitutes) const { |
| 261 SkAutoLockPixels autoLockPixels(fBitmap); | 270 SkBitmap bitmap; |
| 262 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || | 271 image_get_ro_pixels(fImage, &bitmap); |
| 263 fBitmap.getColorTable()); | 272 SkAutoLockPixels autoLockPixels(bitmap); |
| 273 SkASSERT(bitmap.colorType() != kIndex_8_SkColorType || | |
| 274 bitmap.getColorTable()); | |
| 264 | 275 |
| 265 // Write to a temporary buffer to get the compressed length. | 276 // Write to a temporary buffer to get the compressed length. |
| 266 SkDynamicMemoryWStream buffer; | 277 SkDynamicMemoryWStream buffer; |
| 267 SkDeflateWStream deflateWStream(&buffer); | 278 SkDeflateWStream deflateWStream(&buffer); |
| 268 bitmap_alpha_to_a8(fBitmap, &deflateWStream); | 279 bitmap_alpha_to_a8(bitmap, &deflateWStream); |
| 269 deflateWStream.finalize(); // call before detachAsStream(). | 280 deflateWStream.finalize(); // call before detachAsStream(). |
| 270 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 281 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| 271 | 282 |
| 272 SkPDFDict pdfDict("XObject"); | 283 SkPDFDict pdfDict("XObject"); |
| 273 pdfDict.insertName("Subtype", "Image"); | 284 pdfDict.insertName("Subtype", "Image"); |
| 274 pdfDict.insertInt("Width", fBitmap.width()); | 285 pdfDict.insertInt("Width", bitmap.width()); |
| 275 pdfDict.insertInt("Height", fBitmap.height()); | 286 pdfDict.insertInt("Height", bitmap.height()); |
| 276 pdfDict.insertName("ColorSpace", "DeviceGray"); | 287 pdfDict.insertName("ColorSpace", "DeviceGray"); |
| 277 pdfDict.insertInt("BitsPerComponent", 8); | 288 pdfDict.insertInt("BitsPerComponent", 8); |
| 278 pdfDict.insertName("Filter", "FlateDecode"); | 289 pdfDict.insertName("Filter", "FlateDecode"); |
| 279 pdfDict.insertInt("Length", asset->getLength()); | 290 pdfDict.insertInt("Length", asset->getLength()); |
| 280 pdfDict.emitObject(stream, objNumMap, substitutes); | 291 pdfDict.emitObject(stream, objNumMap, substitutes); |
| 281 | 292 |
| 282 pdf_stream_begin(stream); | 293 pdf_stream_begin(stream); |
| 283 stream->writeStream(asset.get(), asset->getLength()); | 294 stream->writeStream(asset.get(), asset->getLength()); |
| 284 pdf_stream_end(stream); | 295 pdf_stream_end(stream); |
| 285 } | 296 } |
| 286 } // namespace | 297 } // namespace |
| 287 | 298 |
| 288 //////////////////////////////////////////////////////////////////////////////// | 299 //////////////////////////////////////////////////////////////////////////////// |
| 289 | 300 |
| 290 namespace { | 301 namespace { |
| 291 class PDFDefaultBitmap : public SkPDFBitmap { | 302 class PDFDefaultBitmap : public SkPDFObject { |
| 292 public: | 303 public: |
| 293 const SkAutoTUnref<SkPDFObject> fSMask; | |
| 294 void emitObject(SkWStream*, | 304 void emitObject(SkWStream*, |
| 295 const SkPDFObjNumMap&, | 305 const SkPDFObjNumMap&, |
| 296 const SkPDFSubstituteMap&) const override; | 306 const SkPDFSubstituteMap&) const override; |
| 297 void addResources(SkPDFObjNumMap*, | 307 void addResources(SkPDFObjNumMap*, |
| 298 const SkPDFSubstituteMap&) const override; | 308 const SkPDFSubstituteMap&) const override; |
| 299 PDFDefaultBitmap(const SkBitmap& bm, SkPDFObject* smask) | 309 PDFDefaultBitmap(const SkImage* image, SkPDFObject* smask) |
| 300 : SkPDFBitmap(bm), fSMask(smask) {} | 310 : fImage(SkRef(image)), fSMask(smask) {} |
| 311 | |
| 312 private: | |
| 313 SkAutoTUnref<const SkImage> fImage; | |
| 314 const SkAutoTUnref<SkPDFObject> fSMask; | |
| 301 }; | 315 }; |
| 302 } // namespace | 316 } // namespace |
| 303 | 317 |
| 304 void PDFDefaultBitmap::addResources( | 318 void PDFDefaultBitmap::addResources( |
| 305 SkPDFObjNumMap* catalog, | 319 SkPDFObjNumMap* catalog, |
| 306 const SkPDFSubstituteMap& substitutes) const { | 320 const SkPDFSubstituteMap& substitutes) const { |
| 307 if (fSMask.get()) { | 321 if (fSMask.get()) { |
| 308 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); | 322 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); |
| 309 SkASSERT(obj); | 323 SkASSERT(obj); |
| 310 if (catalog->addObject(obj)) { | 324 if (catalog->addObject(obj)) { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 336 const SkPMColor* colors = table->readColors(); | 350 const SkPMColor* colors = table->readColors(); |
| 337 for (int i = 0; i < table->count(); i++) { | 351 for (int i = 0; i < table->count(); i++) { |
| 338 pmcolor_to_rgb24(colors[i], tablePtr); | 352 pmcolor_to_rgb24(colors[i], tablePtr); |
| 339 tablePtr += 3; | 353 tablePtr += 3; |
| 340 } | 354 } |
| 341 SkString tableString(tableArray, 3 * table->count()); | 355 SkString tableString(tableArray, 3 * table->count()); |
| 342 result->appendString(tableString); | 356 result->appendString(tableString); |
| 343 return result; | 357 return result; |
| 344 } | 358 } |
| 345 | 359 |
| 346 void PDFDefaultBitmap::emitObject(SkWStream* stream, | 360 void PDFDefaultBitmap::emitObject(SkWStream* stream, |
|
tomhudson
2015/09/30 16:10:29
There's a lot of redundancy between this and PDFAl
hal.canary
2015/10/01 01:24:55
Done.
| |
| 347 const SkPDFObjNumMap& objNumMap, | 361 const SkPDFObjNumMap& objNumMap, |
| 348 const SkPDFSubstituteMap& substitutes) const { | 362 const SkPDFSubstituteMap& substitutes) const { |
| 349 SkAutoLockPixels autoLockPixels(fBitmap); | 363 SkBitmap bitmap; |
| 350 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || | 364 image_get_ro_pixels(fImage, &bitmap); // TODO(halcanary): test |
|
tomhudson
2015/09/30 16:10:29
note TODO?
hal.canary
2015/10/01 01:24:55
Acknowledged.
| |
| 351 fBitmap.getColorTable()); | 365 SkAutoLockPixels autoLockPixels(bitmap); // 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 #ifdef SK_PDF_IMAGE_STATS |
| 446 if (bm.drawsNothing()) { | 455 gJpegImageObjects.fetch_add(1); |
| 447 return nullptr; | 456 #endif |
| 448 } | 457 return new PDFJpegBitmap(info.fSize, data, yuv); |
| 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 } | 458 } |
| 464 } | 459 } |
| 465 | 460 SkPDFObject* smask = |
| 466 SkPDFObject* smask = nullptr; | 461 image->isOpaque() ? nullptr : new PDFAlphaBitmap(image); |
| 467 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { | 462 #ifdef SK_PDF_IMAGE_STATS |
| 468 smask = new PDFAlphaBitmap(bm); | 463 gRegularImageObjects.fetch_add(1); |
| 469 } | 464 #endif |
| 470 SkPDFBitmap* pdfBitmap = new PDFDefaultBitmap(bm, smask); | 465 return new PDFDefaultBitmap(image, smask); |
| 471 canon->addBitmap(pdfBitmap); | |
| 472 return pdfBitmap; | |
| 473 } | 466 } |
| OLD | NEW |