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 |
| 28 bool image_compute_is_opaque(const SkImage* image) { |
| 29 if (image->isOpaque()) { |
| 30 return true; |
| 31 } |
| 32 // keep output PDF small at cost of possible resource use. |
| 33 SkBitmap bm; |
| 34 image_get_ro_pixels(image, &bm); |
| 35 return SkBitmap::ComputeIsOpaque(bm); |
| 36 } |
| 37 |
19 //////////////////////////////////////////////////////////////////////////////// | 38 //////////////////////////////////////////////////////////////////////////////// |
20 | 39 |
21 static void pdf_stream_begin(SkWStream* stream) { | 40 static void pdf_stream_begin(SkWStream* stream) { |
22 static const char streamBegin[] = " stream\n"; | 41 static const char streamBegin[] = " stream\n"; |
23 stream->write(streamBegin, strlen(streamBegin)); | 42 stream->write(streamBegin, strlen(streamBegin)); |
24 } | 43 } |
25 | 44 |
26 static void pdf_stream_end(SkWStream* stream) { | 45 static void pdf_stream_end(SkWStream* stream) { |
27 static const char streamEnd[] = "\nendstream"; | 46 static const char streamEnd[] = "\nendstream"; |
28 stream->write(streamEnd, strlen(streamEnd)); | 47 stream->write(streamEnd, strlen(streamEnd)); |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 return; | 251 return; |
233 case kARGB_4444_SkColorType: | 252 case kARGB_4444_SkColorType: |
234 SkDEBUGFAIL("4444 color type should have been converted to N32"); | 253 SkDEBUGFAIL("4444 color type should have been converted to N32"); |
235 return; | 254 return; |
236 case kUnknown_SkColorType: | 255 case kUnknown_SkColorType: |
237 default: | 256 default: |
238 SkDEBUGFAIL("unexpected color type"); | 257 SkDEBUGFAIL("unexpected color type"); |
239 } | 258 } |
240 } | 259 } |
241 | 260 |
242 //////////////////////////////////////////////////////////////////////////////// | |
243 | |
244 namespace { | |
245 // This SkPDFObject only outputs the alpha layer of the given bitmap. | |
246 class PDFAlphaBitmap : public SkPDFObject { | |
247 public: | |
248 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} | |
249 ~PDFAlphaBitmap() {} | |
250 void emitObject(SkWStream*, | |
251 const SkPDFObjNumMap&, | |
252 const SkPDFSubstituteMap&) const override; | |
253 | |
254 private: | |
255 const SkBitmap fBitmap; | |
256 }; | |
257 | |
258 void PDFAlphaBitmap::emitObject(SkWStream* stream, | |
259 const SkPDFObjNumMap& objNumMap, | |
260 const SkPDFSubstituteMap& substitutes) const { | |
261 SkAutoLockPixels autoLockPixels(fBitmap); | |
262 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || | |
263 fBitmap.getColorTable()); | |
264 | |
265 // Write to a temporary buffer to get the compressed length. | |
266 SkDynamicMemoryWStream buffer; | |
267 SkDeflateWStream deflateWStream(&buffer); | |
268 bitmap_alpha_to_a8(fBitmap, &deflateWStream); | |
269 deflateWStream.finalize(); // call before detachAsStream(). | |
270 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | |
271 | |
272 SkPDFDict pdfDict("XObject"); | |
273 pdfDict.insertName("Subtype", "Image"); | |
274 pdfDict.insertInt("Width", fBitmap.width()); | |
275 pdfDict.insertInt("Height", fBitmap.height()); | |
276 pdfDict.insertName("ColorSpace", "DeviceGray"); | |
277 pdfDict.insertInt("BitsPerComponent", 8); | |
278 pdfDict.insertName("Filter", "FlateDecode"); | |
279 pdfDict.insertInt("Length", asset->getLength()); | |
280 pdfDict.emitObject(stream, objNumMap, substitutes); | |
281 | |
282 pdf_stream_begin(stream); | |
283 stream->writeStream(asset.get(), asset->getLength()); | |
284 pdf_stream_end(stream); | |
285 } | |
286 } // namespace | |
287 | |
288 //////////////////////////////////////////////////////////////////////////////// | |
289 | |
290 namespace { | |
291 class PDFDefaultBitmap : public SkPDFBitmap { | |
292 public: | |
293 const SkAutoTUnref<SkPDFObject> fSMask; | |
294 void emitObject(SkWStream*, | |
295 const SkPDFObjNumMap&, | |
296 const SkPDFSubstituteMap&) const override; | |
297 void addResources(SkPDFObjNumMap*, | |
298 const SkPDFSubstituteMap&) const override; | |
299 PDFDefaultBitmap(const SkBitmap& bm, SkPDFObject* smask) | |
300 : SkPDFBitmap(bm), fSMask(smask) {} | |
301 }; | |
302 } // namespace | |
303 | |
304 void PDFDefaultBitmap::addResources( | |
305 SkPDFObjNumMap* catalog, | |
306 const SkPDFSubstituteMap& substitutes) const { | |
307 if (fSMask.get()) { | |
308 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); | |
309 SkASSERT(obj); | |
310 if (catalog->addObject(obj)) { | |
311 obj->addResources(catalog, substitutes); | |
312 } | |
313 } | |
314 } | |
315 | |
316 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { | 261 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { |
317 SkPDFArray* result = new SkPDFArray; | 262 SkPDFArray* result = new SkPDFArray; |
318 result->reserve(4); | 263 result->reserve(4); |
319 result->appendName("Indexed"); | 264 result->appendName("Indexed"); |
320 result->appendName("DeviceRGB"); | 265 result->appendName("DeviceRGB"); |
321 SkASSERT(table); | 266 SkASSERT(table); |
322 if (table->count() < 1) { | 267 if (table->count() < 1) { |
323 result->appendInt(0); | 268 result->appendInt(0); |
324 char shortTableArray[3] = {0, 0, 0}; | 269 char shortTableArray[3] = {0, 0, 0}; |
325 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray)); | 270 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray)); |
(...skipping 10 matching lines...) Expand all Loading... |
336 const SkPMColor* colors = table->readColors(); | 281 const SkPMColor* colors = table->readColors(); |
337 for (int i = 0; i < table->count(); i++) { | 282 for (int i = 0; i < table->count(); i++) { |
338 pmcolor_to_rgb24(colors[i], tablePtr); | 283 pmcolor_to_rgb24(colors[i], tablePtr); |
339 tablePtr += 3; | 284 tablePtr += 3; |
340 } | 285 } |
341 SkString tableString(tableArray, 3 * table->count()); | 286 SkString tableString(tableArray, 3 * table->count()); |
342 result->appendString(tableString); | 287 result->appendString(tableString); |
343 return result; | 288 return result; |
344 } | 289 } |
345 | 290 |
346 void PDFDefaultBitmap::emitObject(SkWStream* stream, | 291 static void emit_image_xobject(SkWStream* stream, |
347 const SkPDFObjNumMap& objNumMap, | 292 const SkImage* image, |
348 const SkPDFSubstituteMap& substitutes) const { | 293 bool alpha, |
349 SkAutoLockPixels autoLockPixels(fBitmap); | 294 SkPDFObject* smask, |
350 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || | 295 const SkPDFObjNumMap& objNumMap, |
351 fBitmap.getColorTable()); | 296 const SkPDFSubstituteMap& substitutes) { |
| 297 SkBitmap bitmap; |
| 298 image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test |
| 299 SkAutoLockPixels autoLockPixels(bitmap); // with malformed images. |
| 300 SkASSERT(bitmap.colorType() != kIndex_8_SkColorType || |
| 301 bitmap.getColorTable()); |
352 | 302 |
353 // Write to a temporary buffer to get the compressed length. | 303 // Write to a temporary buffer to get the compressed length. |
354 SkDynamicMemoryWStream buffer; | 304 SkDynamicMemoryWStream buffer; |
355 SkDeflateWStream deflateWStream(&buffer); | 305 SkDeflateWStream deflateWStream(&buffer); |
356 bitmap_to_pdf_pixels(fBitmap, &deflateWStream); | 306 if (alpha) { |
| 307 bitmap_alpha_to_a8(bitmap, &deflateWStream); |
| 308 } else { |
| 309 bitmap_to_pdf_pixels(bitmap, &deflateWStream); |
| 310 } |
357 deflateWStream.finalize(); // call before detachAsStream(). | 311 deflateWStream.finalize(); // call before detachAsStream(). |
358 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 312 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
359 | 313 |
360 SkPDFDict pdfDict("XObject"); | 314 SkPDFDict pdfDict("XObject"); |
361 pdfDict.insertName("Subtype", "Image"); | 315 pdfDict.insertName("Subtype", "Image"); |
362 pdfDict.insertInt("Width", fBitmap.width()); | 316 pdfDict.insertInt("Width", bitmap.width()); |
363 pdfDict.insertInt("Height", fBitmap.height()); | 317 pdfDict.insertInt("Height", bitmap.height()); |
364 if (fBitmap.colorType() == kIndex_8_SkColorType) { | 318 if (alpha) { |
365 SkASSERT(1 == pdf_color_component_count(fBitmap.colorType())); | 319 pdfDict.insertName("ColorSpace", "DeviceGray"); |
| 320 } else if (bitmap.colorType() == kIndex_8_SkColorType) { |
| 321 SkASSERT(1 == pdf_color_component_count(bitmap.colorType())); |
366 pdfDict.insertObject("ColorSpace", | 322 pdfDict.insertObject("ColorSpace", |
367 make_indexed_color_space(fBitmap.getColorTable())); | 323 make_indexed_color_space(bitmap.getColorTable())); |
368 } else if (1 == pdf_color_component_count(fBitmap.colorType())) { | 324 } else if (1 == pdf_color_component_count(bitmap.colorType())) { |
369 pdfDict.insertName("ColorSpace", "DeviceGray"); | 325 pdfDict.insertName("ColorSpace", "DeviceGray"); |
370 } else { | 326 } else { |
371 pdfDict.insertName("ColorSpace", "DeviceRGB"); | 327 pdfDict.insertName("ColorSpace", "DeviceRGB"); |
372 } | 328 } |
| 329 if (smask) { |
| 330 pdfDict.insertObjRef("SMask", SkRef(smask)); |
| 331 } |
373 pdfDict.insertInt("BitsPerComponent", 8); | 332 pdfDict.insertInt("BitsPerComponent", 8); |
374 if (fSMask) { | |
375 pdfDict.insertObjRef("SMask", SkRef(fSMask.get())); | |
376 } | |
377 pdfDict.insertName("Filter", "FlateDecode"); | 333 pdfDict.insertName("Filter", "FlateDecode"); |
378 pdfDict.insertInt("Length", asset->getLength()); | 334 pdfDict.insertInt("Length", asset->getLength()); |
379 pdfDict.emitObject(stream, objNumMap, substitutes); | 335 pdfDict.emitObject(stream, objNumMap, substitutes); |
380 | 336 |
381 pdf_stream_begin(stream); | 337 pdf_stream_begin(stream); |
382 stream->writeStream(asset.get(), asset->getLength()); | 338 stream->writeStream(asset.get(), asset->getLength()); |
383 pdf_stream_end(stream); | 339 pdf_stream_end(stream); |
384 } | 340 } |
385 | 341 |
386 //////////////////////////////////////////////////////////////////////////////// | 342 //////////////////////////////////////////////////////////////////////////////// |
387 | 343 |
388 static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) { | 344 namespace { |
389 if (bm.isImmutable()) { | 345 // This SkPDFObject only outputs the alpha layer of the given bitmap. |
390 return bm; | 346 class PDFAlphaBitmap : public SkPDFObject { |
| 347 public: |
| 348 PDFAlphaBitmap(const SkImage* image) : fImage(SkRef(image)) {} |
| 349 ~PDFAlphaBitmap() {} |
| 350 void emitObject(SkWStream* stream, |
| 351 const SkPDFObjNumMap& objNumMap, |
| 352 const SkPDFSubstituteMap& subs) const override { |
| 353 emit_image_xobject(stream, fImage, true, nullptr, objNumMap, subs); |
391 } | 354 } |
392 bm.copyTo(copy); | 355 |
393 copy->setImmutable(); | 356 private: |
394 return *copy; | 357 SkAutoTUnref<const SkImage> fImage; |
395 } | 358 }; |
| 359 |
| 360 } // namespace |
| 361 |
| 362 //////////////////////////////////////////////////////////////////////////////// |
| 363 |
| 364 namespace { |
| 365 class PDFDefaultBitmap : public SkPDFObject { |
| 366 public: |
| 367 void emitObject(SkWStream* stream, |
| 368 const SkPDFObjNumMap& objNumMap, |
| 369 const SkPDFSubstituteMap& substitutes) const override { |
| 370 emit_image_xobject(stream, fImage, false, fSMask, objNumMap, substitutes
); |
| 371 } |
| 372 void addResources(SkPDFObjNumMap* catalog, |
| 373 const SkPDFSubstituteMap& substitutes) const override { |
| 374 if (fSMask.get()) { |
| 375 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); |
| 376 SkASSERT(obj); |
| 377 if (catalog->addObject(obj)) { |
| 378 obj->addResources(catalog, substitutes); |
| 379 } |
| 380 } |
| 381 } |
| 382 PDFDefaultBitmap(const SkImage* image, SkPDFObject* smask) |
| 383 : fImage(SkRef(image)), fSMask(smask) {} |
| 384 |
| 385 private: |
| 386 SkAutoTUnref<const SkImage> fImage; |
| 387 const SkAutoTUnref<SkPDFObject> fSMask; |
| 388 }; |
| 389 } // namespace |
| 390 |
| 391 //////////////////////////////////////////////////////////////////////////////// |
396 | 392 |
397 namespace { | 393 namespace { |
398 /** | 394 /** |
399 * This PDFObject assumes that its constructor was handed YUV JFIF | 395 * This PDFObject assumes that its constructor was handed YUV or |
400 * Jpeg-encoded data that can be directly embedded into a PDF. | 396 * Grayscale JFIF Jpeg-encoded data that can be directly embedded |
| 397 * into a PDF. |
401 */ | 398 */ |
402 class PDFJpegBitmap : public SkPDFBitmap { | 399 class PDFJpegBitmap : public SkPDFObject { |
403 public: | 400 public: |
| 401 SkISize fSize; |
404 SkAutoTUnref<SkData> fData; | 402 SkAutoTUnref<SkData> fData; |
405 bool fIsYUV; | 403 bool fIsYUV; |
406 PDFJpegBitmap(const SkBitmap& bm, SkData* data, bool isYUV) | 404 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV) |
407 : SkPDFBitmap(bm), fData(SkRef(data)), fIsYUV(isYUV) {} | 405 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) {} |
408 void emitObject(SkWStream*, | 406 void emitObject(SkWStream*, |
409 const SkPDFObjNumMap&, | 407 const SkPDFObjNumMap&, |
410 const SkPDFSubstituteMap&) const override; | 408 const SkPDFSubstituteMap&) const override; |
411 }; | 409 }; |
412 | 410 |
413 void PDFJpegBitmap::emitObject(SkWStream* stream, | 411 void PDFJpegBitmap::emitObject(SkWStream* stream, |
414 const SkPDFObjNumMap& objNumMap, | 412 const SkPDFObjNumMap& objNumMap, |
415 const SkPDFSubstituteMap& substituteMap) const { | 413 const SkPDFSubstituteMap& substituteMap) const { |
416 SkPDFDict pdfDict("XObject"); | 414 SkPDFDict pdfDict("XObject"); |
417 pdfDict.insertName("Subtype", "Image"); | 415 pdfDict.insertName("Subtype", "Image"); |
418 pdfDict.insertInt("Width", fBitmap.width()); | 416 pdfDict.insertInt("Width", fSize.width()); |
419 pdfDict.insertInt("Height", fBitmap.height()); | 417 pdfDict.insertInt("Height", fSize.height()); |
420 if (fIsYUV) { | 418 if (fIsYUV) { |
421 pdfDict.insertName("ColorSpace", "DeviceRGB"); | 419 pdfDict.insertName("ColorSpace", "DeviceRGB"); |
422 } else { | 420 } else { |
423 pdfDict.insertName("ColorSpace", "DeviceGray"); | 421 pdfDict.insertName("ColorSpace", "DeviceGray"); |
424 } | 422 } |
425 pdfDict.insertInt("BitsPerComponent", 8); | 423 pdfDict.insertInt("BitsPerComponent", 8); |
426 pdfDict.insertName("Filter", "DCTDecode"); | 424 pdfDict.insertName("Filter", "DCTDecode"); |
427 pdfDict.insertInt("ColorTransform", 0); | 425 pdfDict.insertInt("ColorTransform", 0); |
428 pdfDict.insertInt("Length", SkToInt(fData->size())); | 426 pdfDict.insertInt("Length", SkToInt(fData->size())); |
429 pdfDict.emitObject(stream, objNumMap, substituteMap); | 427 pdfDict.emitObject(stream, objNumMap, substituteMap); |
430 pdf_stream_begin(stream); | 428 pdf_stream_begin(stream); |
431 stream->write(fData->data(), fData->size()); | 429 stream->write(fData->data(), fData->size()); |
432 pdf_stream_end(stream); | 430 pdf_stream_end(stream); |
433 } | 431 } |
434 } // namespace | 432 } // namespace |
435 | 433 |
436 //////////////////////////////////////////////////////////////////////////////// | 434 //////////////////////////////////////////////////////////////////////////////// |
437 | 435 |
438 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) { | 436 SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) { |
439 SkASSERT(canon); | 437 SkAutoTUnref<SkData> data(image->refEncoded()); |
440 if (!SkColorTypeIsValid(bitmap.colorType()) || | 438 SkJFIFInfo info; |
441 kUnknown_SkColorType == bitmap.colorType()) { | 439 if (data && SkIsJFIF(data, &info)) { |
442 return nullptr; | 440 bool yuv = info.fType == SkJFIFInfo::kYCbCr; |
443 } | 441 if (info.fSize == image->dimensions()) { // Sanity check. |
444 SkBitmap copy; | 442 // hold on to data, not image. |
445 const SkBitmap& bm = immutable_bitmap(bitmap, ©); | 443 #ifdef SK_PDF_IMAGE_STATS |
446 if (bm.drawsNothing()) { | 444 gJpegImageObjects.fetch_add(1); |
447 return nullptr; | 445 #endif |
448 } | 446 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 } | 447 } |
464 } | 448 } |
465 | 449 SkPDFObject* smask = |
466 SkPDFObject* smask = nullptr; | 450 image_compute_is_opaque(image) ? nullptr : new PDFAlphaBitmap(image)
; |
467 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { | 451 #ifdef SK_PDF_IMAGE_STATS |
468 smask = new PDFAlphaBitmap(bm); | 452 gRegularImageObjects.fetch_add(1); |
469 } | 453 #endif |
470 SkPDFBitmap* pdfBitmap = new PDFDefaultBitmap(bm, smask); | 454 return new PDFDefaultBitmap(image, smask); |
471 canon->addBitmap(pdfBitmap); | |
472 return pdfBitmap; | |
473 } | 455 } |
OLD | NEW |