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 |