OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 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 "SkCanvas.h" | 8 #include "SkCanvas.h" |
9 #include "SkDevice.h" | 9 #include "SkDevice.h" |
10 #include "SkForceLinking.h" | 10 #include "SkForceLinking.h" |
(...skipping 616 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
627 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_M
ode | 627 xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_M
ode |
628 canvas->drawBitmapRect(sMask, dst, &xfer); | 628 canvas->drawBitmapRect(sMask, dst, &xfer); |
629 canvas->restore(); | 629 canvas->restore(); |
630 } | 630 } |
631 | 631 |
632 canvas->restore(); | 632 canvas->restore(); |
633 | 633 |
634 return kPartial_PdfResult; | 634 return kPartial_PdfResult; |
635 } | 635 } |
636 | 636 |
| 637 //TODO(edisonn): options for implementing isolation and knockout |
| 638 // 1) emulate them (current solution) |
| 639 // PRO: simple |
| 640 // CON: will need to use readPixels, which means serious perf issues |
| 641 // 2) Compile a plan for an array of matrixes, compose the result at the end |
| 642 // PRO: might be faster then 1, no need to readPixels |
| 643 // CON: multiple drawings (but on smaller areas), pay a price at loading pdf
to compute a pdf draw plan |
| 644 // on average, a load with empty draw is 100ms on all the skps we have,
for complete sites |
| 645 // 3) support them natively in SkCanvas |
| 646 // PRO: simple |
| 647 // CON: we would still need to use a form of readPixels anyway, so perf migh
t be the same as 1) |
| 648 // 4) compile a plan using pathops, and render once without any fancy rules with
backdrop |
| 649 // PRO: simple, fast |
| 650 // CON: pathops must be bug free first + time to compute new paths |
| 651 // pay a price at loading pdf to compute a pdf draw plan |
| 652 // on average, a load with empty draw is 100ms on all the skps we have,
for complete sites |
| 653 |
| 654 |
| 655 // TODO(edisonn): draw plan from point! - list of draw ops of a point, like a tr
ee! |
| 656 // TODO(edisonn): Minimal PDF to draw some points - remove everything that it is
not needed, save pdf uncompressed |
| 657 |
| 658 |
| 659 |
| 660 static void doGroup_before(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbox
, SkPdfTransparencyGroupDictionary* tgroup, bool page) { |
| 661 SkRect bboxOrig = bbox; |
| 662 SkBitmap backdrop; |
| 663 bool isolatedGroup = tgroup->I(pdfContext->fPdfDoc); |
| 664 // bool knockoutGroup = tgroup->K(pdfContext->fPdfDoc); |
| 665 bool hasPixels = false; |
| 666 if (!isolatedGroup) { |
| 667 // TODO(edisonn): if the rect is not mapable, the operation could be exp
ensive, e.g. |
| 668 // a diagonal long but small rect would require to save all the page |
| 669 SkMatrix inverse; |
| 670 if (pdfContext->fGraphicsState.fCTM.mapRect(&bbox) && |
| 671 canvas->getTotalMatrix().invert(&inverse) && |
| 672 inverse.mapRect(&bbox)) { |
| 673 SkIRect area = SkIRect::MakeLTRB(SkScalarTruncToInt(bbox.left()), |
| 674 SkScalarTruncToInt(bbox.top()), |
| 675 SkScalarTruncToInt(bbox.right()) +
2, |
| 676 SkScalarTruncToInt(bbox.bottom()) +
2); |
| 677 SkBitmap dummy; |
| 678 if (canvas->readPixels(area, &dummy)) { |
| 679 hasPixels = true; |
| 680 } |
| 681 } |
| 682 } |
| 683 SkPaint paint; |
| 684 pdfContext->fGraphicsState.applyGraphicsState(&paint, false); |
| 685 canvas->saveLayer(&bboxOrig, isolatedGroup ? &paint : NULL); |
| 686 |
| 687 if (hasPixels) { |
| 688 canvas->drawBitmapRect(backdrop, bboxOrig, NULL); |
| 689 } |
| 690 } |
| 691 |
| 692 //static void doGroup_after(PdfContext* pdfContext, SkCanvas* canvas, SkRect bbo
x, SkPdfTransparencyGroupDictionary* tgroup) { |
| 693 //} |
| 694 |
637 static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfT
ype1FormDictionary* skobj) { | 695 static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfT
ype1FormDictionary* skobj) { |
638 if (!skobj || !skobj->hasStream()) { | 696 if (!skobj || !skobj->hasStream()) { |
639 return kIgnoreError_PdfResult; | 697 return kIgnoreError_PdfResult; |
640 } | 698 } |
641 | 699 |
| 700 if (!skobj->has_BBox()) { |
| 701 return kIgnoreError_PdfResult; |
| 702 } |
| 703 |
642 PdfOp_q(pdfContext, canvas, NULL); | 704 PdfOp_q(pdfContext, canvas, NULL); |
643 | 705 |
644 | 706 |
645 | 707 |
646 canvas->save(); | 708 canvas->save(); |
647 | 709 |
648 | 710 |
649 if (skobj->Resources(pdfContext->fPdfDoc)) { | 711 if (skobj->Resources(pdfContext->fPdfDoc)) { |
650 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPd
fDoc); | 712 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPd
fDoc); |
651 } | 713 } |
652 | 714 |
653 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix"); | 715 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix"); |
654 | 716 |
655 if (skobj->has_Matrix()) { | 717 if (skobj->has_Matrix()) { |
656 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdf
Doc)); | 718 pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdf
Doc)); |
657 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM; | 719 pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM; |
658 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM; | 720 pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM; |
659 // TODO(edisonn) reset matrixTm and matricTlm also? | 721 // TODO(edisonn) reset matrixTm and matricTlm also? |
660 } | 722 } |
661 | 723 |
662 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix"); | 724 SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix"); |
663 | 725 |
664 canvas->setMatrix(pdfContext->fGraphicsState.fCTM); | 726 canvas->setMatrix(pdfContext->fGraphicsState.fCTM); |
665 | 727 |
666 if (skobj->has_BBox()) { | 728 SkRect bbox = skobj->BBox(pdfContext->fPdfDoc); |
667 canvas->clipRect(skobj->BBox(pdfContext->fPdfDoc), SkRegion::kIntersect_
Op, true); // TODO(edisonn): AA from settings. | 729 canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA
from settings. |
668 } | |
669 | 730 |
670 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke
nize it as we go. | 731 // TODO(edisonn): iterate smart on the stream even if it is compressed, toke
nize it as we go. |
671 // For this PdfContentsTokenizer needs to be extended. | 732 // For this PdfContentsTokenizer needs to be extended. |
672 | 733 |
673 // This is a group? | 734 // This is a group? |
674 if (skobj->has_Group()) { | 735 if (skobj->has_Group()) { |
675 //TransparencyGroupDictionary* ... | 736 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdf
Doc); |
| 737 doGroup_before(pdfContext, canvas, bbox, tgroup, false); |
676 } | 738 } |
677 | 739 |
678 SkPdfStream* stream = (SkPdfStream*)skobj; | 740 SkPdfStream* stream = (SkPdfStream*)skobj; |
679 | 741 |
680 SkPdfNativeTokenizer* tokenizer = | 742 SkPdfNativeTokenizer* tokenizer = |
681 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageA
llocator); | 743 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageA
llocator); |
682 if (tokenizer != NULL) { | 744 if (tokenizer != NULL) { |
683 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); | 745 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); |
684 looper.loop(); | 746 looper.loop(); |
685 delete tokenizer; | 747 delete tokenizer; |
686 } | 748 } |
687 | 749 |
688 // TODO(edisonn): should we restore the variable stack at the same state? | 750 // TODO(edisonn): should we restore the variable stack at the same state? |
689 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. | 751 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. |
| 752 |
| 753 if (skobj->has_Group()) { |
| 754 canvas->restore(); |
| 755 } |
| 756 |
690 canvas->restore(); | 757 canvas->restore(); |
691 PdfOp_Q(pdfContext, canvas, NULL); | 758 PdfOp_Q(pdfContext, canvas, NULL); |
692 return kPartial_PdfResult; | 759 return kPartial_PdfResult; |
693 } | 760 } |
694 | 761 |
695 //static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const
SkPdfObject* obj) { | 762 //static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const
SkPdfObject* obj) { |
696 // return kNYI_PdfResult; | 763 // return kNYI_PdfResult; |
697 //} | 764 //} |
698 | 765 |
699 PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObjec
t* skobj, SkRect bBox, SkMatrix matrix, double textSize) { | 766 PdfResult doType3Char(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObjec
t* skobj, SkRect bBox, SkMatrix matrix, double textSize) { |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
801 | 868 |
802 if (CheckRecursiveRendering::IsInRendering(skobj)) { | 869 if (CheckRecursiveRendering::IsInRendering(skobj)) { |
803 // Oops, corrupt PDF! | 870 // Oops, corrupt PDF! |
804 return kIgnoreError_PdfResult; | 871 return kIgnoreError_PdfResult; |
805 } | 872 } |
806 CheckRecursiveRendering checkRecursion(skobj); | 873 CheckRecursiveRendering checkRecursion(skobj); |
807 | 874 |
808 | 875 |
809 PdfOp_q(pdfContext, canvas, NULL); | 876 PdfOp_q(pdfContext, canvas, NULL); |
810 | 877 |
811 canvas->save(); | |
812 | 878 |
813 if (skobj->Resources(pdfContext->fPdfDoc)) { | 879 if (skobj->Resources(pdfContext->fPdfDoc)) { |
814 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPd
fDoc); | 880 pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPd
fDoc); |
815 } | 881 } |
816 | 882 |
817 // TODO(edisonn): refactor common path with doXObject() | 883 // TODO(edisonn): MediaBox can be inherited!!!! |
818 // This is a group? | 884 SkRect bbox = skobj->MediaBox(pdfContext->fPdfDoc); |
819 if (skobj->has_Group()) { | 885 if (skobj->has_Group()) { |
820 //TransparencyGroupDictionary* ... | 886 SkPdfTransparencyGroupDictionary* tgroup = skobj->Group(pdfContext->fPdf
Doc); |
| 887 doGroup_before(pdfContext, canvas, bbox, tgroup, true); |
| 888 } else { |
| 889 canvas->save(); |
821 } | 890 } |
822 | 891 |
| 892 |
823 SkPdfNativeTokenizer* tokenizer = | 893 SkPdfNativeTokenizer* tokenizer = |
824 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageA
llocator); | 894 pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageA
llocator); |
825 if (tokenizer != NULL) { | 895 if (tokenizer != NULL) { |
826 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); | 896 PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); |
827 looper.loop(); | 897 looper.loop(); |
828 delete tokenizer; | 898 delete tokenizer; |
829 } | 899 } |
830 | 900 |
831 // TODO(edisonn): should we restore the variable stack at the same state? | 901 // TODO(edisonn): should we restore the variable stack at the same state? |
832 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. | 902 // There could be operands left, that could be consumed by a parent tokenize
r when we pop. |
(...skipping 742 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1575 // TODO(edisonn): alloc memory | 1645 // TODO(edisonn): alloc memory |
1576 return kIgnoreError_PdfResult; | 1646 return kIgnoreError_PdfResult; |
1577 } | 1647 } |
1578 for (int i = 0; i < cnt; i++) { | 1648 for (int i = 0; i < cnt; i++) { |
1579 if (!intervals->objAtAIndex(i)->isNumber()) { | 1649 if (!intervals->objAtAIndex(i)->isNumber()) { |
1580 // TODO(edisonn): report error/warning | 1650 // TODO(edisonn): report error/warning |
1581 return kIgnoreError_PdfResult; | 1651 return kIgnoreError_PdfResult; |
1582 } | 1652 } |
1583 } | 1653 } |
1584 | 1654 |
1585 pdfContext->fGraphicsState.fDashArrayLength = cnt; | |
1586 double total = 0; | 1655 double total = 0; |
1587 for (int i = 0 ; i < cnt; i++) { | 1656 for (int i = 0 ; i < cnt; i++) { |
1588 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->sc
alarValue(); | 1657 pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->sc
alarValue(); |
1589 total += pdfContext->fGraphicsState.fDashArray[i]; | 1658 total += pdfContext->fGraphicsState.fDashArray[i]; |
1590 } | 1659 } |
| 1660 if (cnt & 1) { |
| 1661 if (cnt == 1) { |
| 1662 pdfContext->fGraphicsState.fDashArray[1] = pdfContext->fGraphicsStat
e.fDashArray[0]; |
| 1663 cnt++; |
| 1664 } else { |
| 1665 // TODO(edisonn): report error/warning |
| 1666 return kNYI_PdfResult; |
| 1667 } |
| 1668 } |
| 1669 pdfContext->fGraphicsState.fDashArrayLength = cnt; |
1591 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue(); | 1670 pdfContext->fGraphicsState.fDashPhase = phase->scalarValue(); |
1592 if (pdfContext->fGraphicsState.fDashPhase == 0) { | 1671 if (pdfContext->fGraphicsState.fDashPhase == 0) { |
1593 // other rules, changes? | 1672 // other rules, changes? |
1594 pdfContext->fGraphicsState.fDashPhase = total; | 1673 pdfContext->fGraphicsState.fDashPhase = total; |
1595 } | 1674 } |
1596 | 1675 |
1597 return kOK_PdfResult; | 1676 return kOK_PdfResult; |
1598 } | 1677 } |
1599 | 1678 |
1600 PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) { | 1679 PdfResult skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) { |
(...skipping 783 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2384 | 2463 |
2385 rect = SkRect::MakeWH(width, height); | 2464 rect = SkRect::MakeWH(width, height); |
2386 | 2465 |
2387 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(hei
ght)); | 2466 setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(hei
ght)); |
2388 | 2467 |
2389 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output))); | 2468 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*output))); |
2390 SkCanvas canvas(device); | 2469 SkCanvas canvas(device); |
2391 | 2470 |
2392 return renderer.renderPage(page, &canvas, rect); | 2471 return renderer.renderPage(page, &canvas, rect); |
2393 } | 2472 } |
OLD | NEW |