/** * Cuts the coverage a in 2 parts. * * over will receive the parts where coverage > tresh, while the present * FloatLigne will receive the parts where coverage <= tresh. */ void FloatLigne::Split(FloatLigne *a, float tresh, FloatLigne *over) { Reset(); if ( a->runs.empty() ) { return; } for (int i = 0; i < int(a->runs.size()); i++) { float_ligne_run runA = a->runs[i]; if ( runA.vst >= tresh ) { if ( runA.ven >= tresh ) { if ( over ) { over->AddRun(runA.st, runA.en, runA.vst, runA.ven); } } else { float cutPos = (runA.st * (tresh - runA.ven) + runA.en * (runA.vst - tresh)) / (runA.vst - runA.ven); if ( over ) { over->AddRun(runA.st, cutPos, runA.vst, tresh); } AddRun(cutPos, runA.en, tresh, runA.ven); } } else { if ( runA.ven >= tresh ) { float cutPos = (runA.st * (runA.ven - tresh) + runA.en * (tresh-runA.vst)) / (runA.ven - runA.vst); AddRun(runA.st, cutPos, runA.vst, tresh); if ( over ) { over->AddRun(cutPos, runA.en, tresh, runA.ven); } } else { AddRun(runA.st, runA.en, runA.vst, runA.ven); } } } }
// Verify that text-related properties are captured in run paints. static void TestPaintProps(skiatest::Reporter* reporter) { SkFont font; // Kitchen sink font. font.setSize(42); font.setScaleX(4.2f); font.setTypeface(ToolUtils::create_portable_typeface()); font.setSkewX(0.42f); font.setHinting(SkFontHinting::kFull); font.setEdging(SkFont::Edging::kSubpixelAntiAlias); font.setEmbolden(true); font.setLinearMetrics(true); font.setSubpixel(true); font.setEmbeddedBitmaps(true); font.setForceAutoHinting(true); // Ensure we didn't pick default values by mistake. SkFont defaultFont; REPORTER_ASSERT(reporter, defaultFont.getSize() != font.getSize()); REPORTER_ASSERT(reporter, defaultFont.getScaleX() != font.getScaleX()); REPORTER_ASSERT(reporter, defaultFont.getTypefaceOrDefault() != font.getTypefaceOrDefault()); REPORTER_ASSERT(reporter, defaultFont.getSkewX() != font.getSkewX()); REPORTER_ASSERT(reporter, defaultFont.getHinting() != font.getHinting()); REPORTER_ASSERT(reporter, defaultFont.getEdging() != font.getEdging()); REPORTER_ASSERT(reporter, defaultFont.isEmbolden() != font.isEmbolden()); REPORTER_ASSERT(reporter, defaultFont.isLinearMetrics() != font.isLinearMetrics()); REPORTER_ASSERT(reporter, defaultFont.isSubpixel() != font.isSubpixel()); REPORTER_ASSERT(reporter, defaultFont.isEmbeddedBitmaps() != font.isEmbeddedBitmaps()); REPORTER_ASSERT(reporter, defaultFont.isForceAutoHinting() != font.isForceAutoHinting()); SkTextBlobBuilder builder; AddRun(font, 1, SkTextBlobRunIterator::kDefault_Positioning, SkPoint::Make(0, 0), builder); AddRun(font, 1, SkTextBlobRunIterator::kHorizontal_Positioning, SkPoint::Make(0, 0), builder); AddRun(font, 1, SkTextBlobRunIterator::kFull_Positioning, SkPoint::Make(0, 0), builder); sk_sp<SkTextBlob> blob(builder.make()); SkTextBlobRunIterator it(blob.get()); while (!it.done()) { REPORTER_ASSERT(reporter, it.font() == font); it.next(); } }
/* * Segment a paragraph into runs */ static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph ) { if( p_paragraph->i_size <= 0 ) { msg_Err( p_filter, "ItemizeParagraph() invalid parameters. Paragraph size: %d", p_paragraph->i_size ); return VLC_EGENERIC; } int i_last_run_start = 0; text_style_t *p_last_style = p_paragraph->pp_styles[ 0 ]; #ifdef HAVE_HARFBUZZ hb_script_t last_script = p_paragraph->p_scripts[ 0 ]; FriBidiLevel last_level = p_paragraph->p_levels[ 0 ]; #endif for( int i = 0; i <= p_paragraph->i_size; ++i ) { if( i == p_paragraph->i_size #ifdef HAVE_HARFBUZZ || last_script != p_paragraph->p_scripts[ i ] || last_level != p_paragraph->p_levels[ i ] #endif || p_last_style->i_font_size != p_paragraph->pp_styles[ i ]->i_font_size || ( ( p_last_style->i_style_flags ^ p_paragraph->pp_styles[ i ]->i_style_flags ) & STYLE_HALFWIDTH ) ||!FaceStyleEquals( p_last_style, p_paragraph->pp_styles[ i ] ) ) { int i_ret = AddRun( p_filter, p_paragraph, i_last_run_start, i, 0 ); if( i_ret ) return i_ret; if( i < p_paragraph->i_size ) { i_last_run_start = i; p_last_style = p_paragraph->pp_styles[ i ]; #ifdef HAVE_HARFBUZZ last_script = p_paragraph->p_scripts[ i ]; last_level = p_paragraph->p_levels[ i ]; #endif } } } return VLC_SUCCESS; }
static void RunBuilderTest(skiatest::Reporter* reporter, SkTextBlobBuilder& builder, const RunDef in[], unsigned inCount, const RunDef out[], unsigned outCount) { SkFont font; unsigned glyphCount = 0; unsigned posCount = 0; for (unsigned i = 0; i < inCount; ++i) { AddRun(font, in[i].count, in[i].pos, SkPoint::Make(in[i].x, in[i].y), builder); glyphCount += in[i].count; posCount += in[i].count * in[i].pos; } sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, (inCount > 0) == SkToBool(blob)); if (!blob) { return; } SkTextBlobRunIterator it(blob.get()); for (unsigned i = 0; i < outCount; ++i) { REPORTER_ASSERT(reporter, !it.done()); REPORTER_ASSERT(reporter, out[i].pos == it.positioning()); REPORTER_ASSERT(reporter, out[i].count == it.glyphCount()); if (SkTextBlobRunIterator::kDefault_Positioning == out[i].pos) { REPORTER_ASSERT(reporter, out[i].x == it.offset().x()); REPORTER_ASSERT(reporter, out[i].y == it.offset().y()); } else if (SkTextBlobRunIterator::kHorizontal_Positioning == out[i].pos) { REPORTER_ASSERT(reporter, out[i].y == it.offset().y()); } for (unsigned k = 0; k < it.glyphCount(); ++k) { REPORTER_ASSERT(reporter, k % 128 == it.glyphs()[k]); if (SkTextBlobRunIterator::kHorizontal_Positioning == it.positioning()) { REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k]); } else if (SkTextBlobRunIterator::kFull_Positioning == it.positioning()) { REPORTER_ASSERT(reporter, SkIntToScalar(k % 128) == it.pos()[k * 2]); REPORTER_ASSERT(reporter, -SkIntToScalar(k % 128) == it.pos()[k * 2 + 1]); } } it.next(); } REPORTER_ASSERT(reporter, it.done()); }
/// Extract the parts where coverage > tresh. void FloatLigne::Over(FloatLigne *a, float tresh) { Reset(); if ( a->runs.empty() ) { return; } bool startExists = false; float lastStart = 0; float lastEnd = 0; for (int i = 0; i < int(a->runs.size()); i++) { float_ligne_run runA = a->runs[i]; if ( runA.vst >= tresh ) { if ( runA.ven >= tresh ) { if ( startExists ) { if ( lastEnd >= runA.st - 0.00001 ) { lastEnd = runA.en; } else { AddRun(lastStart, lastEnd, tresh, tresh); lastStart = runA.st; lastEnd = runA.en; } } else { lastStart = runA.st; lastEnd = runA.en; } startExists = true; } else { float cutPos = (runA.st * (tresh - runA.ven) + runA.en * (runA.vst - tresh)) / (runA.vst - runA.ven); if ( startExists ) { if ( lastEnd >= runA.st - 0.00001 ) { AddRun(lastStart, cutPos, tresh, tresh); } else { AddRun(lastStart, lastEnd, tresh, tresh); AddRun(runA.st, cutPos, tresh, tresh); } } else { AddRun(runA.st, cutPos, tresh, tresh); } startExists = false; } } else { if ( runA.ven >= tresh ) { float cutPos = (runA.st * (runA.ven - tresh) + runA.en * (tresh - runA.vst)) / (runA.ven - runA.vst); if ( startExists ) { AddRun(lastStart, lastEnd, tresh, tresh); } startExists = true; lastStart = cutPos; lastEnd = runA.en; } else { if ( startExists ) { AddRun(lastStart, lastEnd, tresh, tresh); } startExists = false; } } } if ( startExists ) { AddRun(lastStart, lastEnd, tresh, tresh); } }
int FloatLigne::AddRun(float st, float en, float vst, float ven) { return AddRun(st, en, vst, ven, (ven - vst) / (en - st)); }
/** * Extract a set of non-overlapping runs from the boundaries. * * We scan the boundaries left to right, maintaining a set of coverage * portions currently being scanned. For each such portion, the function * will add the index of its first boundary in an array; but instead of * allocating another array, it uses a field in float_ligne_bord: pend_ind. * The outcome is that an array of float_ligne_run is produced. */ void FloatLigne::Flatten() { if ( int(bords.size()) <= 1 ) { Reset(); return; } runs.clear(); // qsort(bords,bords.size(),sizeof(float_ligne_bord),FloatLigne::CmpBord); // SortBords(0,bords.size()-1); float totPente = 0; float totStart = 0; float totX = bords[0].pos; bool startExists = false; float lastStart = 0; float lastVal = 0; int pending = 0; // for (int i=0;i<bords.size();) { // read the list from left to right, adding a run for each boundary crossed, minus runs with alpha=0 for (int i=/*0*/s_first; i>=0 && i < int(bords.size()) ;) { float cur = bords[i].pos; // position of the current boundary (there may be several boundaries at this position) float leftV = 0; // deltas in coverage value at this position float rightV = 0; float leftP = 0; // deltas in coverage increase per unit length at this position float rightP = 0; // more precisely, leftV is the sum of decreases of coverage value, // while rightV is the sum of increases, so that leftV+rightV is the delta. // idem for leftP and rightP // start by scanning all boundaries that end a portion at this position while ( i >= 0 && i < int(bords.size()) && bords[i].pos == cur && bords[i].start == false ) { leftV += bords[i].val; leftP += bords[i].pente; #ifndef faster_flatten // we need to remove the boundary that started this coverage portion for the pending list if ( bords[i].other >= 0 && bords[i].other < int(bords.size()) ) { // so we use the pend_inv "array" int const k = bords[bords[i].other].pend_inv; if ( k >= 0 && k < pending ) { // and update the pend_ind array and its inverse pend_inv bords[k].pend_ind = bords[pending - 1].pend_ind; bords[bords[k].pend_ind].pend_inv = k; } } #endif // one less portion pending pending--; // and we move to the next boundary in the doubly linked list i=bords[i].s_next; //i++; } // then scan all boundaries that start a portion at this position while ( i >= 0 && i < int(bords.size()) && bords[i].pos == cur && bords[i].start == true ) { rightV += bords[i].val; rightP += bords[i].pente; #ifndef faster_flatten bords[pending].pend_ind=i; bords[i].pend_inv=pending; #endif pending++; i = bords[i].s_next; //i++; } // coverage value at end of the run will be "start coverage"+"delta per unit length"*"length" totStart = totStart + totPente * (cur - totX); if ( startExists ) { // add that run AddRun(lastStart, cur, lastVal, totStart, totPente); } // update "delta coverage per unit length" totPente += rightP - leftP; // not really needed here totStart += rightV - leftV; // update position totX = cur; if ( pending > 0 ) { startExists = true; #ifndef faster_flatten // to avoid accumulation of numerical errors, we compute an accurate coverage for this position "cur" totStart = RemainingValAt(cur, pending); #endif lastVal = totStart; lastStart = cur; } else { startExists = false; totStart = 0; totPente = 0; } } }
static int ShapeParagraphHarfBuzz( filter_t *p_filter, paragraph_t **p_old_paragraph ) { paragraph_t *p_paragraph = *p_old_paragraph; paragraph_t *p_new_paragraph = 0; filter_sys_t *p_sys = p_filter->p_sys; int i_total_glyphs = 0; int i_ret = VLC_EGENERIC; if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 ) { msg_Err( p_filter, "ShapeParagraphHarfBuzz() invalid parameters. " "Paragraph size: %d. Runs count %d", p_paragraph->i_size, p_paragraph->i_runs_count ); return VLC_EGENERIC; } for( int i = 0; i < p_paragraph->i_runs_count; ++i ) { run_desc_t *p_run = p_paragraph->p_runs + i; text_style_t *p_style = p_run->p_style; /* * When using HarfBuzz, this is where font faces are loaded. * In the other two paths (shaping with FriBidi or no * shaping at all), faces are loaded in LoadGlyphs() */ FT_Face p_face = 0; if( !p_run->p_face ) { p_face = LoadFace( p_filter, p_style ); if( !p_face ) { p_face = p_sys->p_face; p_style = &p_sys->style; p_run->p_style = p_style; } p_run->p_face = p_face; } else p_face = p_run->p_face; p_run->p_hb_font = hb_ft_font_create( p_face, 0 ); if( !p_run->p_hb_font ) { msg_Err( p_filter, "ShapeParagraphHarfBuzz(): hb_ft_font_create() error" ); goto error; } p_run->p_buffer = hb_buffer_create(); if( !p_run->p_buffer ) { msg_Err( p_filter, "ShapeParagraphHarfBuzz(): hb_buffer_create() error" ); goto error; } hb_buffer_set_direction( p_run->p_buffer, p_run->direction ); hb_buffer_set_script( p_run->p_buffer, p_run->script ); #ifdef __OS2__ hb_buffer_add_utf16( p_run->p_buffer, p_paragraph->p_code_points + p_run->i_start_offset, p_run->i_end_offset - p_run->i_start_offset, 0, p_run->i_end_offset - p_run->i_start_offset ); #else hb_buffer_add_utf32( p_run->p_buffer, p_paragraph->p_code_points + p_run->i_start_offset, p_run->i_end_offset - p_run->i_start_offset, 0, p_run->i_end_offset - p_run->i_start_offset ); #endif hb_shape( p_run->p_hb_font, p_run->p_buffer, 0, 0 ); p_run->p_glyph_infos = hb_buffer_get_glyph_infos( p_run->p_buffer, &p_run->i_glyph_count ); p_run->p_glyph_positions = hb_buffer_get_glyph_positions( p_run->p_buffer, &p_run->i_glyph_count ); if( p_run->i_glyph_count <= 0 ) { msg_Err( p_filter, "ShapeParagraphHarfBuzz() invalid glyph count in shaped run" ); goto error; } i_total_glyphs += p_run->i_glyph_count; } p_new_paragraph = NewParagraph( p_filter, i_total_glyphs, 0, 0, 0, p_paragraph->i_runs_size ); if( !p_new_paragraph ) { i_ret = VLC_ENOMEM; goto error; } p_new_paragraph->paragraph_type = p_paragraph->paragraph_type; int i_index = 0; for( int i = 0; i < p_paragraph->i_runs_count; ++i ) { run_desc_t *p_run = p_paragraph->p_runs + i; hb_glyph_info_t *p_infos = p_run->p_glyph_infos; hb_glyph_position_t *p_positions = p_run->p_glyph_positions; for( unsigned int j = 0; j < p_run->i_glyph_count; ++j ) { /* * HarfBuzz reverses the order of glyphs in RTL runs. We reverse * it again here to keep the glyphs in their logical order. * For line breaking of paragraphs to work correctly, visual * reordering should be done after line breaking has taken * place. */ int i_run_index = p_run->direction == HB_DIRECTION_LTR ? j : p_run->i_glyph_count - 1 - j; int i_source_index = p_infos[ i_run_index ].cluster + p_run->i_start_offset; p_new_paragraph->p_code_points[ i_index ] = 0; p_new_paragraph->pi_glyph_indices[ i_index ] = p_infos[ i_run_index ].codepoint; p_new_paragraph->p_scripts[ i_index ] = p_paragraph->p_scripts[ i_source_index ]; p_new_paragraph->p_types[ i_index ] = p_paragraph->p_types[ i_source_index ]; p_new_paragraph->p_levels[ i_index ] = p_paragraph->p_levels[ i_source_index ]; p_new_paragraph->pp_styles[ i_index ] = p_paragraph->pp_styles[ i_source_index ]; p_new_paragraph->pi_karaoke_bar[ i_index ] = p_paragraph->pi_karaoke_bar[ i_source_index ]; p_new_paragraph->p_glyph_bitmaps[ i_index ].i_x_offset = p_positions[ i_run_index ].x_offset; p_new_paragraph->p_glyph_bitmaps[ i_index ].i_y_offset = p_positions[ i_run_index ].y_offset; p_new_paragraph->p_glyph_bitmaps[ i_index ].i_x_advance = p_positions[ i_run_index ].x_advance; p_new_paragraph->p_glyph_bitmaps[ i_index ].i_y_advance = p_positions[ i_run_index ].y_advance; ++i_index; } if( AddRun( p_filter, p_new_paragraph, i_index - p_run->i_glyph_count, i_index, p_run->p_face ) ) goto error; } for( int i = 0; i < p_paragraph->i_runs_count; ++i ) { hb_font_destroy( p_paragraph->p_runs[ i ].p_hb_font ); hb_buffer_destroy( p_paragraph->p_runs[ i ].p_buffer ); } FreeParagraph( *p_old_paragraph ); *p_old_paragraph = p_new_paragraph; return VLC_SUCCESS; error: for( int i = 0; i < p_paragraph->i_runs_count; ++i ) { if( p_paragraph->p_runs[ i ].p_hb_font ) hb_font_destroy( p_paragraph->p_runs[ i ].p_hb_font ); if( p_paragraph->p_runs[ i ].p_buffer ) hb_buffer_destroy( p_paragraph->p_runs[ i ].p_buffer ); } if( p_new_paragraph ) FreeParagraph( p_new_paragraph ); return i_ret; }
// write current pattern to file using extended RLE format const char *writerle(std::ostream &os, char *comments, lifealgo &imp, int top, int left, int bottom, int right, bool xrle) { badwrite = false; if (xrle) { // write out #CXRLE line; note that the XRLE indicator is prefixed // with #C so apps like Life32 and MCell will ignore the line os << "#CXRLE Pos=" << left << ',' << top; if (imp.getGeneration() > bigint::zero) os << " Gen=" << imp.getGeneration().tostring('\0'); os << '\n'; } char *endcomms = NULL; if (comments && comments[0]) { // write given comment line(s) -- can't just do fputs(comments,f) // because comments might include arbitrary text after the "!" char *p = comments; while (*p == '#') { while (*p != '\n') p++; p++; } if (p != comments) { char savech = *p; *p = '\0'; os << comments; *p = savech; } // any comment lines not starting with # will be written after "!" if (*p != '\0') endcomms = p; } if ( imp.isEmpty() || top > bottom || left > right ) { // empty pattern os << "x = 0, y = 0, rule = " << imp.getrule() << "\n!\n"; } else { // do header line unsigned int wd = right - left + 1; unsigned int ht = bottom - top + 1; sprintf(outbuff, "x = %u, y = %u, rule = %s\n", wd, ht, imp.getrule()); outpos = strlen(outbuff); // do RLE data unsigned int linelen = 0; unsigned int brun = 0; unsigned int orun = 0; unsigned int dollrun = 0; int laststate = WRLE_NONE ; int multistate = imp.NumCellStates() > 2 ; int cx, cy; // for showing accurate progress we need to add pattern height to pop count // in case this is a huge pattern with many blank rows double maxcount = imp.getPopulation().todouble() + ht; double accumcount = 0; int currcount = 0; int v = 0 ; for ( cy=top; cy<=bottom; cy++ ) { // set lastchar to anything except 'o' or 'b' laststate = WRLE_NONE ; currcount++; for ( cx=left; cx<=right; cx++ ) { int skip = imp.nextcell(cx, cy, v); if (skip + cx > right) skip = -1; // pretend we found no more live cells if (skip > 0) { // have exactly "skip" dead cells here if (laststate == 0) { brun += skip; } else { if (orun > 0) { // output current run of live cells AddRun(os, laststate, multistate, orun, linelen); } laststate = 0 ; brun = skip; } } if (skip >= 0) { // found next live cell in this row cx += skip; if (laststate == v) { orun++; } else { if (dollrun > 0) // output current run of $ chars AddRun(os, WRLE_NEWLINE, multistate, dollrun, linelen); if (brun > 0) // output current run of dead cells AddRun(os, 0, multistate, brun, linelen); if (orun > 0) AddRun(os, laststate, multistate, orun, linelen) ; laststate = v ; orun = 1; } currcount++; } else { cx = right + 1; // done this row } if (currcount > 1024) { char msg[128]; accumcount += currcount; currcount = 0; sprintf(msg, "File size: %.2f MB", os.tellp() / 1048576.0); if (lifeabortprogress(accumcount / maxcount, msg)) break; } } // end of current row if (isaborted()) break; if (laststate == 0) // forget dead cells at end of row brun = 0; else if (laststate >= 0) // output current run of live cells AddRun(os, laststate, multistate, orun, linelen); dollrun++; } // terminate RLE data dollrun = 1; AddRun(os, WRLE_EOP, multistate, dollrun, linelen); putchar('\n', os); // flush outbuff if (outpos > 0 && !badwrite && !os.write(outbuff, outpos)) badwrite = true; } if (endcomms) os << endcomms; if (badwrite) return "Failed to write output buffer!"; else return 0; }
void Paragraph::Add(const Run& run) { AddRun(run); }