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 |