Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: src/pdf/SkPDFBitmap.cpp

Issue 1372783003: SkPDF: Implement drawImage*() properly (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 2015-09-30 (Wednesday) 21:22:24 EDT Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pdf/SkPDFBitmap.h ('k') | src/pdf/SkPDFCanon.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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, &copy); 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 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFBitmap.h ('k') | src/pdf/SkPDFCanon.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698