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

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

Issue 1025773002: PDF: Correctly embed JPEG images directly into PDF output. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 2015-04-17 (Friday) 16:19:22 EDT Created 5 years, 8 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') | tests/PDFJpegEmbedTest.cpp » ('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 "SkFlate.h" 10 #include "SkFlate.h"
11 #include "SkImageGenerator.h"
10 #include "SkPDFBitmap.h" 12 #include "SkPDFBitmap.h"
11 #include "SkPDFCanon.h" 13 #include "SkPDFCanon.h"
14 #include "SkPixelRef.h"
12 #include "SkStream.h" 15 #include "SkStream.h"
13 #include "SkUnPreMultiply.h" 16 #include "SkUnPreMultiply.h"
14 17
15 //////////////////////////////////////////////////////////////////////////////// 18 ////////////////////////////////////////////////////////////////////////////////
16 19
17 static void pdf_stream_begin(SkWStream* stream) { 20 static void pdf_stream_begin(SkWStream* stream) {
18 static const char streamBegin[] = " stream\n"; 21 static const char streamBegin[] = " stream\n";
19 stream->write(streamBegin, strlen(streamBegin)); 22 stream->write(streamBegin, strlen(streamBegin));
20 } 23 }
21 24
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 pdfDict.emitObject(stream, objNumMap, substitutes); 279 pdfDict.emitObject(stream, objNumMap, substitutes);
277 280
278 pdf_stream_begin(stream); 281 pdf_stream_begin(stream);
279 stream->writeStream(asset.get(), asset->getLength()); 282 stream->writeStream(asset.get(), asset->getLength());
280 pdf_stream_end(stream); 283 pdf_stream_end(stream);
281 } 284 }
282 } // namespace 285 } // namespace
283 286
284 //////////////////////////////////////////////////////////////////////////////// 287 ////////////////////////////////////////////////////////////////////////////////
285 288
286 void SkPDFBitmap::addResources(SkPDFObjNumMap* catalog, 289 namespace {
287 const SkPDFSubstituteMap& substitutes) const { 290 class PDFDefaultBitmap : public SkPDFBitmap {
291 public:
292 const SkAutoTUnref<SkPDFObject> fSMask;
293 void emitObject(SkWStream*,
294 const SkPDFObjNumMap&,
295 const SkPDFSubstituteMap&) override;
296 void addResources(SkPDFObjNumMap*,
297 const SkPDFSubstituteMap&) const override;
298 PDFDefaultBitmap(const SkBitmap& bm, SkPDFObject* smask)
299 : SkPDFBitmap(bm), fSMask(smask) {}
300 };
301 } // namespace
302
303 void PDFDefaultBitmap::addResources(
304 SkPDFObjNumMap* catalog,
305 const SkPDFSubstituteMap& substitutes) const {
288 if (fSMask.get()) { 306 if (fSMask.get()) {
289 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get()); 307 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get());
290 SkASSERT(obj); 308 SkASSERT(obj);
291 if (catalog->addObject(obj)) { 309 if (catalog->addObject(obj)) {
292 obj->addResources(catalog, substitutes); 310 obj->addResources(catalog, substitutes);
293 } 311 }
294 } 312 }
295 } 313 }
296 314
297 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { 315 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
(...skipping 19 matching lines...) Expand all
317 const SkPMColor* colors = table->readColors(); 335 const SkPMColor* colors = table->readColors();
318 for (int i = 0; i < table->count(); i++) { 336 for (int i = 0; i < table->count(); i++) {
319 pmcolor_to_rgb24(colors[i], tablePtr); 337 pmcolor_to_rgb24(colors[i], tablePtr);
320 tablePtr += 3; 338 tablePtr += 3;
321 } 339 }
322 SkString tableString(tableArray, 3 * table->count()); 340 SkString tableString(tableArray, 3 * table->count());
323 result->append(new SkPDFString(tableString))->unref(); 341 result->append(new SkPDFString(tableString))->unref();
324 return result; 342 return result;
325 } 343 }
326 344
327 void SkPDFBitmap::emitObject(SkWStream* stream, 345 void PDFDefaultBitmap::emitObject(SkWStream* stream,
328 const SkPDFObjNumMap& objNumMap, 346 const SkPDFObjNumMap& objNumMap,
329 const SkPDFSubstituteMap& substitutes) { 347 const SkPDFSubstituteMap& substitutes) {
330 SkAutoLockPixels autoLockPixels(fBitmap); 348 SkAutoLockPixels autoLockPixels(fBitmap);
331 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || 349 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
332 fBitmap.getColorTable()); 350 fBitmap.getColorTable());
333 351
334 // Write to a temporary buffer to get the compressed length. 352 // Write to a temporary buffer to get the compressed length.
335 SkDynamicMemoryWStream buffer; 353 SkDynamicMemoryWStream buffer;
336 SkDeflateWStream deflateWStream(&buffer); 354 SkDeflateWStream deflateWStream(&buffer);
337 bitmap_to_pdf_pixels(fBitmap, &deflateWStream); 355 bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
338 deflateWStream.finalize(); // call before detachAsStream(). 356 deflateWStream.finalize(); // call before detachAsStream().
339 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); 357 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
(...skipping 10 matching lines...) Expand all
350 pdfDict.insertName("ColorSpace", "DeviceGray"); 368 pdfDict.insertName("ColorSpace", "DeviceGray");
351 } else { 369 } else {
352 pdfDict.insertName("ColorSpace", "DeviceRGB"); 370 pdfDict.insertName("ColorSpace", "DeviceRGB");
353 } 371 }
354 pdfDict.insertInt("BitsPerComponent", 8); 372 pdfDict.insertInt("BitsPerComponent", 8);
355 if (fSMask) { 373 if (fSMask) {
356 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); 374 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
357 } 375 }
358 pdfDict.insertName("Filter", "FlateDecode"); 376 pdfDict.insertName("Filter", "FlateDecode");
359 pdfDict.insertInt("Length", asset->getLength()); 377 pdfDict.insertInt("Length", asset->getLength());
360 pdfDict.emitObject(stream, objNumMap,substitutes); 378 pdfDict.emitObject(stream, objNumMap, substitutes);
361 379
362 pdf_stream_begin(stream); 380 pdf_stream_begin(stream);
363 stream->writeStream(asset.get(), asset->getLength()); 381 stream->writeStream(asset.get(), asset->getLength());
364 pdf_stream_end(stream); 382 pdf_stream_end(stream);
365 } 383 }
366 384
367 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
368 SkPDFObject* smask)
369 : fBitmap(bm), fSMask(smask) {}
370
371 SkPDFBitmap::~SkPDFBitmap() {}
372
373 //////////////////////////////////////////////////////////////////////////////// 385 ////////////////////////////////////////////////////////////////////////////////
374 386
375 static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) { 387 static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
376 if (bm.isImmutable()) { 388 if (bm.isImmutable()) {
377 return bm; 389 return bm;
378 } 390 }
379 bm.copyTo(copy); 391 bm.copyTo(copy);
380 copy->setImmutable(); 392 copy->setImmutable();
381 return *copy; 393 return *copy;
382 } 394 }
383 395
396 namespace {
397 /**
398 * This PDFObject assumes that its constructor was handed YUV JFIF
399 * Jpeg-encoded data that can be directly embedded into a PDF.
400 */
401 class PDFJpegBitmap : public SkPDFBitmap {
402 public:
403 SkAutoTUnref<SkData> fData;
404 PDFJpegBitmap(const SkBitmap& bm, SkData* data)
405 : SkPDFBitmap(bm), fData(SkRef(data)) {}
406 void emitObject(SkWStream*,
407 const SkPDFObjNumMap&,
408 const SkPDFSubstituteMap&) override;
409 };
410
411 void PDFJpegBitmap::emitObject(SkWStream* stream,
412 const SkPDFObjNumMap& objNumMap,
413 const SkPDFSubstituteMap& substituteMap) {
414 SkPDFDict pdfDict("XObject");
415 pdfDict.insertName("Subtype", "Image");
416 pdfDict.insertInt("Width", fBitmap.width());
417 pdfDict.insertInt("Height", fBitmap.height());
418 pdfDict.insertName("ColorSpace", "DeviceRGB");
419 pdfDict.insertInt("BitsPerComponent", 8);
420 pdfDict.insertName("Filter", "DCTDecode");
421 pdfDict.insertInt("ColorTransform", 0);
422 pdfDict.insertInt("Length", SkToInt(fData->size()));
423 pdfDict.emitObject(stream, objNumMap, substituteMap);
424 pdf_stream_begin(stream);
425 stream->write(fData->data(), fData->size());
426 pdf_stream_end(stream);
427 }
428 } // namespace
429
430 ////////////////////////////////////////////////////////////////////////////////
431
432 static bool is_jfif_yuv_jpeg(SkData* data) {
433 const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0};
434 const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0};
435 // 0 1 2 3 4 5 6 7 8 9 10
436 // FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ...
437 if (data->size() < 11 ||
438 0 != memcmp(data->bytes(), bytesZeroToThree,
439 sizeof(bytesZeroToThree)) ||
440 0 != memcmp(data->bytes() + 6, bytesSixToTen, sizeof(bytesSixToTen))) {
441 return false;
442 }
443 SkAutoTDelete<SkImageGenerator> gen(SkImageGenerator::NewFromData(data));
444 SkISize sizes[3];
445 // Only YUV JPEG allows access to YUV planes.
446 return gen && gen->getYUV8Planes(sizes, NULL, NULL, NULL);
447 }
448
384 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) { 449 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
385 SkASSERT(canon); 450 SkASSERT(canon);
386 if (!SkColorTypeIsValid(bitmap.colorType()) || 451 if (!SkColorTypeIsValid(bitmap.colorType()) ||
387 kUnknown_SkColorType == bitmap.colorType()) { 452 kUnknown_SkColorType == bitmap.colorType()) {
388 return NULL; 453 return NULL;
389 } 454 }
390 SkBitmap copy; 455 SkBitmap copy;
391 const SkBitmap& bm = immutable_bitmap(bitmap, &copy); 456 const SkBitmap& bm = immutable_bitmap(bitmap, &copy);
392 if (bm.drawsNothing()) { 457 if (bm.drawsNothing()) {
393 return NULL; 458 return NULL;
394 } 459 }
395 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) { 460 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
396 return SkRef(canonBitmap); 461 return SkRef(canonBitmap);
397 } 462 }
463
464 if (bm.pixelRef() && bm.pixelRefOrigin().isZero() &&
465 bm.dimensions() == bm.pixelRef()->info().dimensions()) {
466 // Requires the bitmap to be backed by lazy pixels.
467 SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData());
468 if (data && is_jfif_yuv_jpeg(data)) {
469 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFJpegBitmap, (bm, data));
470 canon->addBitmap(pdfBitmap);
471 return pdfBitmap;
472 }
473 }
474
398 SkPDFObject* smask = NULL; 475 SkPDFObject* smask = NULL;
399 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { 476 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
400 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); 477 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
401 } 478 }
402 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask)); 479 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFDefaultBitmap, (bm, smask));
403 canon->addBitmap(pdfBitmap); 480 canon->addBitmap(pdfBitmap);
404 return pdfBitmap; 481 return pdfBitmap;
405 } 482 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFBitmap.h ('k') | tests/PDFJpegEmbedTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698