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

Side by Side Diff: third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp

Issue 2740083002: Add support for shaper-driven line breaking to HarfBuzzShaper (Closed)
Patch Set: Try to fix test failures Created 3 years, 8 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
OLDNEW
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
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/SmallCapsIterator.h" 43 #include "platform/fonts/SmallCapsIterator.h"
38 #include "platform/fonts/UTF16TextIterator.h" 44 #include "platform/fonts/UTF16TextIterator.h"
39 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" 45 #include "platform/fonts/opentype/OpenTypeCapsSupport.h"
40 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" 46 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h"
41 #include "platform/fonts/shaping/HarfBuzzFace.h" 47 #include "platform/fonts/shaping/HarfBuzzFace.h"
42 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" 48 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h"
43 #include "platform/text/TextBreakIterator.h" 49 #include "platform/text/TextBreakIterator.h"
44 #include "wtf/Compiler.h" 50 #include "wtf/Compiler.h"
45 #include "wtf/MathExtras.h" 51 #include "wtf/MathExtras.h"
46 #include "wtf/PtrUtil.h" 52 #include "wtf/PtrUtil.h"
47 #include "wtf/text/Unicode.h" 53 #include "wtf/text/Unicode.h"
48 #include <algorithm>
49 #include <hb.h>
50 #include <memory>
51 #include <unicode/uchar.h>
52 #include <unicode/uscript.h>
53 54
54 namespace blink { 55 namespace blink {
55 enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange }; 56 enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange };
56 57
57 struct HolesQueueItem { 58 struct HolesQueueItem {
58 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); 59 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
59 HolesQueueItemAction m_action; 60 HolesQueueItemAction m_action;
60 unsigned m_startIndex; 61 unsigned m_startIndex;
61 unsigned m_numCharacters; 62 unsigned m_numCharacters;
62 HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num) 63 HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num)
(...skipping 637 matching lines...) Expand 10 before | Expand all | Expand 10 after
700 if (start < segmentRange.end && end > segmentRange.start) 701 if (start < segmentRange.end && end > segmentRange.start)
701 shapeSegment(&rangeData, segmentRange, result.get()); 702 shapeSegment(&rangeData, segmentRange, result.get());
702 } 703 }
703 return result.release(); 704 return result.release();
704 } 705 }
705 706
706 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font, 707 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font,
707 TextDirection direction) const { 708 TextDirection direction) const {
708 return shape(font, direction, 0, m_textLength); 709 return shape(font, direction, 0, m_textLength);
709 } 710 }
711
712 namespace {
713
714 unsigned previousBreakOpportunity(unsigned start,
715 unsigned offset,
716 unsigned textLength,
717 LazyLineBreakIterator& breakIterator,
718 LineBreakType breakType) {
719 unsigned pos = std::min(start + offset, textLength);
720 for (; pos > start; pos--) {
721 int nextBreak = 0;
722 if (breakIterator.isBreakable(pos, nextBreak, breakType))
723 return pos;
724 }
725 return start;
726 }
727
728 unsigned nextBreakOpportunity(unsigned offset,
729 LazyLineBreakIterator& breakIterator,
730 LineBreakType breakType) {
731 int nextBreak = 0;
732 breakIterator.isBreakable(offset, nextBreak, breakType);
733 return nextBreak;
734 }
735
736 unsigned previousSafeToBreakAfter(const UChar* text,
737 unsigned start,
738 unsigned offset) {
739 // TODO: This should use HarfBuzz safe to break info when available. For now
740 // it'll scan back until the previous space character or something.
741 for (; offset > start; offset--) {
742 if (text[offset - 1] == spaceCharacter)
743 break;
744 }
745 return offset;
746 }
747
748 unsigned nextSafeToBreakBefore(const UChar* text,
749 unsigned end,
750 unsigned offset) {
751 for (; offset < end; offset++) {
752 if (text[offset] == spaceCharacter)
753 break;
754 }
755 return offset;
756 }
757
758 } // namespace
759
760 // Shapes a line of text by finding a valid and appropriate break opportunity
761 // based on the shaping results for the entire paragraph. Re-shapes the start
762 // and end of the line as needed.
763 //
764 // Definitions:
765 // Candidate break opportunity: Ideal point to break, disregarding line
766 // breaking rules. May be in the middle of a word
767 // or inside a ligature.
768 // Valid break opportunity: A point where a break is allowed according to
769 // the relevant breaking rules.
770 // Safe-to-break: A point where a break may occur without
771 // affecting the rendering or metrics of the
772 // text. Breaking at safe-to-break point does not
773 // require reshaping.
774 //
775 // For example:
776 // Given the string "Line breaking example", an available space of 100px and a
777 // mono-space font where each glyph is 10px wide.
778 //
779 // Line breaking example
780 // | |
781 // 0 100px
782 //
783 // The candidate (or ideal) break opportunity would be at an offset of 10 as
784 // the break would happen at exactly 100px in that case.
785 // The previous valid break opportunity though is at an offset of 5.
786 // If we further assume that the font kerns with space then even though it's a
787 // valid break opportunity reshaping is required as the combined width of the
788 // two segments "Line " and "breaking" may be different from "Line breaking".
789 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeLine(const Font* font,
790 const ShapeResult* result,
791 unsigned startOffset,
792 const AtomicString locale,
793 LineBreakType breakType,
794 LayoutUnit availableSpace,
795 unsigned* breakOffset) const {
796 // The start position in the original shape results.
797 LayoutUnit startPosition = result->snappedStartPositionForOffset(startOffset);
798
799 // If the start offset is not at a safe-to-break boundary the content between
800 // the start and the next safe-to-break boundary needs to be reshaped and the
801 // available space adjusted to take the reshaping into account.
802 RefPtr<ShapeResult> lineStartResult;
803 unsigned firstSafe = nextSafeToBreakBefore(m_text, m_textLength, startOffset);
804 if (firstSafe != startOffset) {
805 LayoutUnit originalWidth =
806 result->snappedEndPositionForOffset(firstSafe) - startPosition;
807 lineStartResult = shape(font, result->direction(), startOffset, firstSafe);
808 availableSpace += lineStartResult->snappedWidth() - originalWidth;
809 }
810
811 // Find a candidate break opportunity by identifying the last offset before
812 // exceeding the available space and the determine the closest valid break
813 // preceding the candidate.
814 LazyLineBreakIterator breakIterator(String(m_text, m_textLength), locale);
815 LayoutUnit endPosition = startPosition + availableSpace;
816 unsigned candidateBreak = result->offsetForPosition(endPosition, false);
817 unsigned breakOpportunity = previousBreakOpportunity(
818 startOffset, candidateBreak, m_textLength, breakIterator, breakType);
819 if (breakOpportunity <= startOffset) {
820 breakOpportunity =
821 nextBreakOpportunity(candidateBreak, breakIterator, breakType);
822 }
823
824 RefPtr<ShapeResult> lineEndResult;
825 unsigned lastSafe = breakOpportunity;
826 while (breakOpportunity > startOffset) {
827 // If the previous valid break opportunity is not at a safe-to-break
828 // boundary reshape between the safe-to-break offset and the valid break
829 // offset. If the resulting width exceeds the available space the
830 // preceding boundary is tried until the available space is sufficient.
831 unsigned previousSafe = std::max(
832 previousSafeToBreakAfter(m_text, startOffset, breakOpportunity),
833 startOffset);
834 if (previousSafe != breakOpportunity) {
835 LayoutUnit safePosition =
836 result->snappedStartPositionForOffset(previousSafe);
837 while (breakOpportunity > previousSafe && previousSafe > startOffset) {
838 lineEndResult =
839 shape(font, result->direction(), previousSafe, breakOpportunity);
840 if (safePosition + lineEndResult->snappedWidth() <= endPosition)
841 break;
842 lineEndResult = nullptr;
843 breakOpportunity =
844 previousBreakOpportunity(startOffset, breakOpportunity - 1,
845 m_textLength, breakIterator, breakType);
846 }
847 }
848
849 if (breakOpportunity > startOffset) {
850 lastSafe = previousSafe;
851 break;
852 }
853
854 // No suitable break opportunity, not exceeding the available space,
855 // found. Choose the next valid one even though it will overflow.
856 breakOpportunity =
857 nextBreakOpportunity(candidateBreak, breakIterator, breakType);
858 }
859
860 // Create shape results for the line by copying from the re-shaped result (if
861 // reshaping was needed) and the original shape results.
862 RefPtr<ShapeResult> lineResult =
863 ShapeResult::create(font, 0, result->direction());
864 unsigned maxLength = std::numeric_limits<unsigned>::max();
865 if (lineStartResult)
866 lineStartResult->copyRange(0, maxLength, lineResult.get());
867 if (lastSafe > firstSafe)
868 result->copyRange(firstSafe, lastSafe, lineResult.get());
869 if (lineEndResult)
870 lineEndResult->copyRange(lastSafe, maxLength, lineResult.get());
871
872 *breakOffset = breakOpportunity;
873 return lineResult.release();
874 }
875
710 } // namespace blink 876 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698