Chromium Code Reviews| 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 "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 Loading... | |
| 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 Loading... | |
| 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, ©); | 440 const SkBitmap& bm = immutable_bitmap(bitmap, ©); |
| 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 } |
| OLD | NEW |