Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 Google Inc. All rights reserved. | 2 * Copyright (c) 2012 Google Inc. All rights reserved. |
| 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. | 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
| 7 * met: | 7 * met: |
| 8 * | 8 * |
| 9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 */ | 30 */ |
| 31 | 31 |
| 32 #include "platform/fonts/shaping/HarfBuzzShaper.h" | 32 #include "platform/fonts/shaping/HarfBuzzShaper.h" |
| 33 | 33 |
| 34 #include <hb.h> | |
| 35 #include <unicode/uchar.h> | |
| 36 #include <unicode/uscript.h> | |
| 37 #include <algorithm> | |
| 38 #include <memory> | |
| 39 #include "platform/LayoutUnit.h" | |
| 34 #include "platform/fonts/Font.h" | 40 #include "platform/fonts/Font.h" |
| 35 #include "platform/fonts/FontDescription.h" | 41 #include "platform/fonts/FontDescription.h" |
| 36 #include "platform/fonts/FontFallbackIterator.h" | 42 #include "platform/fonts/FontFallbackIterator.h" |
| 37 #include "platform/fonts/GlyphBuffer.h" | 43 #include "platform/fonts/GlyphBuffer.h" |
| 38 #include "platform/fonts/SmallCapsIterator.h" | 44 #include "platform/fonts/SmallCapsIterator.h" |
| 39 #include "platform/fonts/UTF16TextIterator.h" | 45 #include "platform/fonts/UTF16TextIterator.h" |
| 40 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" | 46 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" |
| 41 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" | 47 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" |
| 42 #include "platform/fonts/shaping/HarfBuzzFace.h" | 48 #include "platform/fonts/shaping/HarfBuzzFace.h" |
| 43 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" | 49 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
| 44 #include "platform/text/TextBreakIterator.h" | 50 #include "platform/text/TextBreakIterator.h" |
| 45 #include "wtf/Compiler.h" | 51 #include "wtf/Compiler.h" |
| 46 #include "wtf/MathExtras.h" | 52 #include "wtf/MathExtras.h" |
| 47 #include "wtf/PtrUtil.h" | 53 #include "wtf/PtrUtil.h" |
| 48 #include "wtf/text/Unicode.h" | 54 #include "wtf/text/Unicode.h" |
| 49 #include <algorithm> | |
| 50 #include <hb.h> | |
| 51 #include <memory> | |
| 52 #include <unicode/uchar.h> | |
| 53 #include <unicode/uscript.h> | |
| 54 | 55 |
| 55 namespace blink { | 56 namespace blink { |
| 56 enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange }; | 57 enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange }; |
| 57 | 58 |
| 58 struct HolesQueueItem { | 59 struct HolesQueueItem { |
| 59 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); | 60 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); |
| 60 HolesQueueItemAction m_action; | 61 HolesQueueItemAction m_action; |
| 61 unsigned m_startIndex; | 62 unsigned m_startIndex; |
| 62 unsigned m_numCharacters; | 63 unsigned m_numCharacters; |
| 63 HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num) | 64 HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num) |
| (...skipping 637 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 701 if (start < segmentRange.end && end > segmentRange.start) | 702 if (start < segmentRange.end && end > segmentRange.start) |
| 702 shapeSegment(&rangeData, segmentRange, result.get()); | 703 shapeSegment(&rangeData, segmentRange, result.get()); |
| 703 } | 704 } |
| 704 return result.release(); | 705 return result.release(); |
| 705 } | 706 } |
| 706 | 707 |
| 707 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font, | 708 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font, |
| 708 TextDirection direction) const { | 709 TextDirection direction) const { |
| 709 return shape(font, direction, 0, m_textLength); | 710 return shape(font, direction, 0, m_textLength); |
| 710 } | 711 } |
| 712 | |
| 713 namespace { | |
| 714 | |
| 715 unsigned previousBreakOpportunity(unsigned start, | |
| 716 unsigned offset, | |
| 717 unsigned textLength, | |
| 718 LazyLineBreakIterator& breakIterator, | |
| 719 LineBreakType breakType) { | |
| 720 unsigned pos = std::min(start + offset, textLength); | |
| 721 for (; pos > start; pos--) { | |
| 722 int nextBreak = 0; | |
| 723 if (breakIterator.isBreakable(pos, nextBreak, breakType)) | |
| 724 return pos; | |
| 725 } | |
| 726 return start; | |
| 727 } | |
| 728 | |
| 729 unsigned nextBreakOpportunity(unsigned offset, | |
| 730 LazyLineBreakIterator& breakIterator, | |
| 731 LineBreakType breakType) { | |
| 732 int nextBreak = 0; | |
| 733 breakIterator.isBreakable(offset, nextBreak, breakType); | |
| 734 return nextBreak; | |
| 735 } | |
| 736 | |
| 737 unsigned previousSafeToBreakAfter(const UChar* text, | |
| 738 unsigned start, | |
| 739 unsigned offset) { | |
| 740 // TODO: This should use harf-buzz safe to break info when available. For now | |
|
drott
2017/03/22 21:01:11
s/harf-buzz/HarfBuzz/
| |
| 741 // it'll scan back until the previous space character or something. | |
| 742 for (; offset > start; offset--) { | |
| 743 if (text[offset - 1] == spaceCharacter) | |
| 744 break; | |
| 745 } | |
| 746 return offset; | |
| 747 } | |
| 748 | |
| 749 unsigned nextSafeToBreakBefore(const UChar* text, | |
| 750 unsigned end, | |
| 751 unsigned offset) { | |
| 752 for (; offset < end; offset++) { | |
| 753 if (text[offset] == spaceCharacter) | |
| 754 break; | |
| 755 } | |
| 756 return offset; | |
| 757 } | |
| 758 | |
| 759 } // namespace | |
| 760 | |
| 761 // Shapes a line of text by finding a valid and appropriate break opportunity | |
| 762 // based on the shaping results for the entire paragraph. Re-shapes the start | |
| 763 // and end of the line as needed. | |
| 764 // | |
| 765 // Definitions: | |
| 766 // Candidate break opportunity: Ideal point to break, disregarding line | |
| 767 // breaking rules. May be in the middle of a word | |
| 768 // or inside a ligature. | |
| 769 // Valid break opportunity: A point where a break is allowed according to | |
| 770 // the relevant breaking rules. | |
| 771 // Safe-to-break: A point where a break may occur without | |
| 772 // affecting the rendering or metrics of the | |
| 773 // text. Breaking at safe-to-break point does not | |
| 774 // require reshaping. | |
| 775 // | |
| 776 // For example: | |
| 777 // Given the string "Line breaking example", an available space of 100px and a | |
| 778 // mono-space font where each glyph is 10px wide. | |
| 779 // | |
| 780 // Line breaking example | |
| 781 // | | | |
| 782 // 0 100px | |
| 783 // | |
| 784 // The candidate (or ideal) break opportunity would be at an offset of 10 as | |
| 785 // the break would happen at exactly 100px in that case. | |
| 786 // The previous valid break opportunity though is at an offset of 5. | |
| 787 // If we further assume that the font kerns with space then even though it's a | |
| 788 // valid break opportunity reshaping is required as the combined width of the | |
| 789 // two segments "Line " and "breaking" may be different from "Line breaking". | |
| 790 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeLine(const Font* font, | |
| 791 const ShapeResult* result, | |
| 792 unsigned startOffset, | |
| 793 const AtomicString locale, | |
| 794 LineBreakType breakType, | |
| 795 LayoutUnit availableSpace, | |
| 796 unsigned* breakOffset) const { | |
| 797 // The start position in the original shape results. | |
| 798 LayoutUnit startPosition = result->snappedStartPositionForOffset(startOffset); | |
|
drott
2017/03/22 21:01:11
IIUC, this one is pixel snapped, why is that the c
eae
2017/03/27 20:14:02
No, this snaps to the nearest 1/64th of a pixel, n
drott
2017/03/27 20:24:36
I see, that makes more sense, thank you for the ex
| |
| 799 | |
| 800 // If the start offset is not at a safe-to-break boundary content between | |
|
drott
2017/03/22 21:01:11
What do you mean by "content" here? Or probably th
eae
2017/03/27 20:14:02
Missing "the", thank you.
| |
| 801 // the start and the next safe-to-break boundary needs to be reshaped and the | |
| 802 // available space adjusted to take the reshaping into account. | |
| 803 RefPtr<ShapeResult> lineStartResult; | |
| 804 unsigned firstSafe = nextSafeToBreakBefore(m_text, m_textLength, startOffset); | |
| 805 if (firstSafe != startOffset) { | |
| 806 LayoutUnit originalWidth = | |
| 807 result->snappedEndPositionForOffset(firstSafe) - startPosition; | |
| 808 lineStartResult = shape(font, result->direction(), startOffset, firstSafe); | |
| 809 availableSpace += lineStartResult->snappedWidth() - originalWidth; | |
| 810 } | |
| 811 | |
| 812 // Find a candidate break opportunity by identifying the last offset before | |
| 813 // exceeding the available space and the determine the closest valid break | |
| 814 // preceding the candidate. | |
| 815 LazyLineBreakIterator breakIterator(String(m_text, m_textLength), locale); | |
| 816 LayoutUnit endPosition = startPosition + availableSpace; | |
| 817 unsigned candidateBreak = result->offsetForPosition(endPosition, false); | |
| 818 unsigned breakOpportunity = previousBreakOpportunity( | |
| 819 startOffset, candidateBreak, m_textLength, breakIterator, breakType); | |
| 820 if (breakOpportunity <= startOffset) { | |
| 821 breakOpportunity = | |
| 822 nextBreakOpportunity(candidateBreak, breakIterator, breakType); | |
| 823 } | |
| 824 | |
| 825 RefPtr<ShapeResult> lineEndResult; | |
| 826 unsigned lastSafe = breakOpportunity; | |
| 827 while (breakOpportunity > startOffset) { | |
| 828 // If the previous valid break opportunity is not at a safe-to-break | |
| 829 // boundary reshape between the safe-to-break offset and the valid break | |
| 830 // offset. If the resulting width exceeds the available space the | |
| 831 // preceding boundary is tried until the available space is sufficient. | |
| 832 unsigned previousSafe = std::max( | |
|
drott
2017/03/22 21:01:11
Nice.
| |
| 833 previousSafeToBreakAfter(m_text, startOffset, breakOpportunity), | |
| 834 startOffset); | |
| 835 if (previousSafe != breakOpportunity) { | |
| 836 LayoutUnit safePosition = | |
| 837 result->snappedStartPositionForOffset(previousSafe); | |
| 838 while (breakOpportunity > previousSafe && previousSafe > startOffset) { | |
| 839 lineEndResult = | |
| 840 shape(font, result->direction(), previousSafe, breakOpportunity); | |
| 841 if (safePosition + lineEndResult->snappedWidth() <= endPosition) | |
| 842 break; | |
| 843 lineEndResult = nullptr; | |
| 844 breakOpportunity = | |
| 845 previousBreakOpportunity(startOffset, breakOpportunity - 1, | |
| 846 m_textLength, breakIterator, breakType); | |
| 847 } | |
| 848 } | |
| 849 | |
| 850 if (breakOpportunity > startOffset) { | |
| 851 lastSafe = previousSafe; | |
| 852 break; | |
| 853 } | |
| 854 | |
| 855 // No suitable break opportunity, not exceeding the available space, | |
| 856 // found. Choose the next valid one even though it will overflow. | |
| 857 breakOpportunity = | |
| 858 nextBreakOpportunity(candidateBreak, breakIterator, breakType); | |
| 859 } | |
| 860 | |
| 861 // Create shape results for the line by copying from the re-shaped result (if | |
| 862 // reshaping was needed) and the original shape results. | |
| 863 RefPtr<ShapeResult> lineResult = | |
| 864 ShapeResult::create(font, 0, result->direction()); | |
| 865 unsigned maxLength = std::numeric_limits<unsigned>::max(); | |
| 866 if (lineStartResult) | |
| 867 lineStartResult->copyRange(0, maxLength, lineResult.get()); | |
| 868 if (lastSafe > firstSafe) | |
| 869 result->copyRange(firstSafe, lastSafe, lineResult.get()); | |
| 870 if (lineEndResult) | |
| 871 lineEndResult->copyRange(lastSafe, maxLength, lineResult.get()); | |
| 872 | |
| 873 *breakOffset = breakOpportunity; | |
| 874 return lineResult.release(); | |
| 875 } | |
| 876 | |
| 711 } // namespace blink | 877 } // namespace blink |
| OLD | NEW |