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

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-03-25 (Wednesday) 16:28:39 EDT Created 5 years, 9 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"
12 #include "SkPDFCatalog.h" 14 #include "SkPDFCatalog.h"
15 #include "SkPixelRef.h"
13 #include "SkStream.h" 16 #include "SkStream.h"
14 #include "SkUnPreMultiply.h" 17 #include "SkUnPreMultiply.h"
15 18
16 //////////////////////////////////////////////////////////////////////////////// 19 ////////////////////////////////////////////////////////////////////////////////
17 20
18 static void pdf_stream_begin(SkWStream* stream) { 21 static void pdf_stream_begin(SkWStream* stream) {
19 static const char streamBegin[] = " stream\n"; 22 static const char streamBegin[] = " stream\n";
20 stream->write(streamBegin, strlen(streamBegin)); 23 stream->write(streamBegin, strlen(streamBegin));
21 } 24 }
22 25
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 namespace { 244 namespace {
242 // This SkPDFObject only outputs the alpha layer of the given bitmap. 245 // This SkPDFObject only outputs the alpha layer of the given bitmap.
243 class PDFAlphaBitmap : public SkPDFObject { 246 class PDFAlphaBitmap : public SkPDFObject {
244 public: 247 public:
245 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} 248 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
246 ~PDFAlphaBitmap() {} 249 ~PDFAlphaBitmap() {}
247 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; 250 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
248 251
249 private: 252 private:
250 const SkBitmap fBitmap; 253 const SkBitmap fBitmap;
251 void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
252 }; 254 };
253 255
254 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { 256 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
255 SkAutoLockPixels autoLockPixels(fBitmap); 257 SkAutoLockPixels autoLockPixels(fBitmap);
256 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType || 258 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
257 fBitmap.getColorTable()); 259 fBitmap.getColorTable());
258 260
259 // Write to a temporary buffer to get the compressed length. 261 // Write to a temporary buffer to get the compressed length.
260 SkDynamicMemoryWStream buffer; 262 SkDynamicMemoryWStream buffer;
261 SkDeflateWStream deflateWStream(&buffer); 263 SkDeflateWStream deflateWStream(&buffer);
262 bitmap_alpha_to_a8(fBitmap, &deflateWStream); 264 bitmap_alpha_to_a8(fBitmap, &deflateWStream);
263 deflateWStream.finalize(); // call before detachAsStream(). 265 deflateWStream.finalize(); // call before detachAsStream().
264 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); 266 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
265 267
266 this->emitDict(stream, catalog, asset->getLength());
267 pdf_stream_begin(stream);
268 stream->writeStream(asset.get(), asset->getLength());
269 pdf_stream_end(stream);
270 }
271
272 void PDFAlphaBitmap::emitDict(SkWStream* stream,
273 SkPDFCatalog* catalog,
274 size_t length) const {
275 SkPDFDict pdfDict("XObject"); 268 SkPDFDict pdfDict("XObject");
276 pdfDict.insertName("Subtype", "Image"); 269 pdfDict.insertName("Subtype", "Image");
277 pdfDict.insertInt("Width", fBitmap.width()); 270 pdfDict.insertInt("Width", fBitmap.width());
278 pdfDict.insertInt("Height", fBitmap.height()); 271 pdfDict.insertInt("Height", fBitmap.height());
279 pdfDict.insertName("ColorSpace", "DeviceGray"); 272 pdfDict.insertName("ColorSpace", "DeviceGray");
280 pdfDict.insertInt("BitsPerComponent", 8); 273 pdfDict.insertInt("BitsPerComponent", 8);
281 pdfDict.insertName("Filter", "FlateDecode"); 274 pdfDict.insertName("Filter", "FlateDecode");
282 pdfDict.insertInt("Length", length); 275 pdfDict.insertInt("Length", asset->getLength());
283 pdfDict.emitObject(stream, catalog); 276 pdfDict.emitObject(stream, catalog);
277
278 pdf_stream_begin(stream);
279 stream->writeStream(asset.get(), asset->getLength());
280 pdf_stream_end(stream);
284 } 281 }
285 } // namespace 282 } // namespace
286 283
287 //////////////////////////////////////////////////////////////////////////////// 284 ////////////////////////////////////////////////////////////////////////////////
288 285
289 void SkPDFBitmap::addResources(SkPDFCatalog* catalog) const { 286 namespace {
287 class PDFBitmap : public SkPDFBitmap {
mtklein 2015/03/25 21:36:53 Oh come on.
hal.canary 2015/04/17 18:57:13 Done.
288 public:
289 const SkAutoTUnref<SkPDFObject> fSMask;
290 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
291 void addResources(SkPDFCatalog*) const SK_OVERRIDE;
292 PDFBitmap(const SkBitmap& bm, SkPDFObject* smask)
293 : SkPDFBitmap(bm), fSMask(smask) {}
294 };
295 } // namespace
296
297 void PDFBitmap::addResources(SkPDFCatalog* catalog) const {
290 if (fSMask.get()) { 298 if (fSMask.get()) {
291 if (catalog->addObject(fSMask.get())) { 299 if (catalog->addObject(fSMask.get())) {
292 fSMask->addResources(catalog); 300 fSMask->addResources(catalog);
293 } 301 }
294 } 302 }
295 } 303 }
296 304
297 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
298 SkAutoLockPixels autoLockPixels(fBitmap);
299 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
300 fBitmap.getColorTable());
301
302 // Write to a temporary buffer to get the compressed length.
303 SkDynamicMemoryWStream buffer;
304 SkDeflateWStream deflateWStream(&buffer);
305 bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
306 deflateWStream.finalize(); // call before detachAsStream().
307 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
308
309 this->emitDict(stream, catalog, asset->getLength());
310 pdf_stream_begin(stream);
311 stream->writeStream(asset.get(), asset->getLength());
312 pdf_stream_end(stream);
313 }
314
315 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { 305 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
316 SkPDFArray* result = SkNEW(SkPDFArray); 306 SkPDFArray* result = SkNEW(SkPDFArray);
317 result->reserve(4); 307 result->reserve(4);
318 result->appendName("Indexed"); 308 result->appendName("Indexed");
319 result->appendName("DeviceRGB"); 309 result->appendName("DeviceRGB");
320 SkASSERT(table); 310 SkASSERT(table);
321 if (table->count() < 1) { 311 if (table->count() < 1) {
322 result->appendInt(0); 312 result->appendInt(0);
323 char shortTableArray[3] = {0, 0, 0}; 313 char shortTableArray[3] = {0, 0, 0};
324 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray)); 314 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
(...skipping 10 matching lines...) Expand all
335 const SkPMColor* colors = table->readColors(); 325 const SkPMColor* colors = table->readColors();
336 for (int i = 0; i < table->count(); i++) { 326 for (int i = 0; i < table->count(); i++) {
337 pmcolor_to_rgb24(colors[i], tablePtr); 327 pmcolor_to_rgb24(colors[i], tablePtr);
338 tablePtr += 3; 328 tablePtr += 3;
339 } 329 }
340 SkString tableString(tableArray, 3 * table->count()); 330 SkString tableString(tableArray, 3 * table->count());
341 result->append(new SkPDFString(tableString))->unref(); 331 result->append(new SkPDFString(tableString))->unref();
342 return result; 332 return result;
343 } 333 }
344 334
345 void SkPDFBitmap::emitDict(SkWStream* stream, 335 void PDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
346 SkPDFCatalog* catalog, 336 SkAutoLockPixels autoLockPixels(fBitmap);
347 size_t length) const { 337 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
338 fBitmap.getColorTable());
339
340 // Write to a temporary buffer to get the compressed length.
341 SkDynamicMemoryWStream buffer;
342 SkDeflateWStream deflateWStream(&buffer);
343 bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
344 deflateWStream.finalize(); // call before detachAsStream().
345 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
346
348 SkPDFDict pdfDict("XObject"); 347 SkPDFDict pdfDict("XObject");
349 pdfDict.insertName("Subtype", "Image"); 348 pdfDict.insertName("Subtype", "Image");
350 pdfDict.insertInt("Width", fBitmap.width()); 349 pdfDict.insertInt("Width", fBitmap.width());
351 pdfDict.insertInt("Height", fBitmap.height()); 350 pdfDict.insertInt("Height", fBitmap.height());
352 if (fBitmap.colorType() == kIndex_8_SkColorType) { 351 if (fBitmap.colorType() == kIndex_8_SkColorType) {
353 SkASSERT(1 == pdf_color_component_count(fBitmap.colorType())); 352 SkASSERT(1 == pdf_color_component_count(fBitmap.colorType()));
354 pdfDict.insert("ColorSpace", make_indexed_color_space( 353 pdfDict.insert("ColorSpace", make_indexed_color_space(
355 fBitmap.getColorTable()))->unref(); 354 fBitmap.getColorTable()))->unref();
356 } else if (1 == pdf_color_component_count(fBitmap.colorType())) { 355 } else if (1 == pdf_color_component_count(fBitmap.colorType())) {
357 pdfDict.insertName("ColorSpace", "DeviceGray"); 356 pdfDict.insertName("ColorSpace", "DeviceGray");
358 } else { 357 } else {
359 pdfDict.insertName("ColorSpace", "DeviceRGB"); 358 pdfDict.insertName("ColorSpace", "DeviceRGB");
360 } 359 }
361 pdfDict.insertInt("BitsPerComponent", 8); 360 pdfDict.insertInt("BitsPerComponent", 8);
362 if (fSMask) { 361 if (fSMask) {
363 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); 362 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
364 } 363 }
365 pdfDict.insertName("Filter", "FlateDecode"); 364 pdfDict.insertName("Filter", "FlateDecode");
366 pdfDict.insertInt("Length", length); 365 pdfDict.insertInt("Length", asset->getLength());
367 pdfDict.emitObject(stream, catalog); 366 pdfDict.emitObject(stream, catalog);
367
368 pdf_stream_begin(stream);
369 stream->writeStream(asset.get(), asset->getLength());
370 pdf_stream_end(stream);
368 } 371 }
369 372
370 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
371 SkPDFObject* smask)
372 : fBitmap(bm), fSMask(smask) {}
373
374 SkPDFBitmap::~SkPDFBitmap() {}
375
376 //////////////////////////////////////////////////////////////////////////////// 373 ////////////////////////////////////////////////////////////////////////////////
377 374
378 static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) { 375 static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
379 if (bm.isImmutable()) { 376 if (bm.isImmutable()) {
380 return bm; 377 return bm;
381 } 378 }
382 bm.copyTo(copy); 379 bm.copyTo(copy);
383 copy->setImmutable(); 380 copy->setImmutable();
384 return *copy; 381 return *copy;
385 } 382 }
386 383
384 namespace {
385 /**
386 * This PDFObject assumes that its constructor was handed YUV JFIF
387 * Jpeg-encoded data that can be directly embedded into a PDF.
388 */
389 class PDFJpegBitmap : public SkPDFBitmap {
390 public:
391 SkAutoTUnref<SkData> fData;
392 PDFJpegBitmap(const SkBitmap& bm, SkData* data)
393 : SkPDFBitmap(bm), fData(SkRef(data)) {}
394 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
395 };
396
397 void PDFJpegBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
398 SkPDFDict pdfDict("XObject");
399 pdfDict.insertName("Subtype", "Image");
400 pdfDict.insertInt("Width", fBitmap.width());
401 pdfDict.insertInt("Height", fBitmap.height());
402 pdfDict.insertName("ColorSpace", "DeviceRGB");
403 pdfDict.insertInt("BitsPerComponent", 8);
404 pdfDict.insertName("Filter", "DCTDecode");
405 pdfDict.insertInt("ColorTransform", 0);
406 pdfDict.insertInt("Length", SkToInt(fData->size()));
407 pdfDict.emitObject(stream, catalog);
408 pdf_stream_begin(stream);
409 stream->write(fData->data(), fData->size());
410 pdf_stream_end(stream);
411 }
412 } // namespace
413
414 ////////////////////////////////////////////////////////////////////////////////
415
416 static bool is_jfif_yuv_jpeg(SkData* data) {
417 const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0};
418 const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0};
419 // 0 1 2 3 4 5 6 7 8 9 10
420 // FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ...
421 if (data->size() < 11 ||
422 0 != memcmp(data->bytes(), bytesZeroToThree,
423 sizeof(bytesZeroToThree)) ||
424 0 != memcmp(data->bytes() + 6, bytesSixToTen, sizeof(bytesSixToTen))) {
425 return false;
426 }
427 SkAutoTDelete<SkImageGenerator> gen(SkImageGenerator::NewFromData(data));
428 SkISize sizes[3];
429 // Only YUV JPEG allows access to YUV planes.
430 return gen && gen->getYUV8Planes(sizes, NULL, NULL, NULL);
431 }
432
387 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) { 433 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
388 SkASSERT(canon); 434 SkASSERT(canon);
389 if (!SkColorTypeIsValid(bitmap.colorType()) || 435 if (!SkColorTypeIsValid(bitmap.colorType()) ||
390 kUnknown_SkColorType == bitmap.colorType()) { 436 kUnknown_SkColorType == bitmap.colorType()) {
391 return NULL; 437 return NULL;
392 } 438 }
393 SkBitmap copy; 439 SkBitmap copy;
394 const SkBitmap& bm = immutable_bitmap(bitmap, &copy); 440 const SkBitmap& bm = immutable_bitmap(bitmap, &copy);
395 if (bm.drawsNothing()) { 441 if (bm.drawsNothing()) {
396 return NULL; 442 return NULL;
397 } 443 }
398 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) { 444 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
399 return SkRef(canonBitmap); 445 return SkRef(canonBitmap);
400 } 446 }
447
448 if (bm.pixelRef() && bm.pixelRefOrigin().isZero() &&
449 bm.dimensions() == bm.pixelRef()->info().dimensions()) {
450 // Requires the bitmap to be backed by lazy pixels.
451 SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData());
452 if (data && is_jfif_yuv_jpeg(data)) {
453 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFJpegBitmap, (bm, data));
454 canon->addBitmap(pdfBitmap);
455 return pdfBitmap;
456 }
457 }
458
401 SkPDFObject* smask = NULL; 459 SkPDFObject* smask = NULL;
402 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { 460 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
403 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); 461 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
404 } 462 }
405 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask)); 463 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFBitmap, (bm, smask));
406 canon->addBitmap(pdfBitmap); 464 canon->addBitmap(pdfBitmap);
407 return pdfBitmap; 465 return pdfBitmap;
408 } 466 }
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