void Polygon2f::Shrink(float distance, list<Polygon2f*> &parentPolygons, list<Polygon2f*> &polygons) { list<Polygon2f*> emptyPolygons; list<Polygon2f*> rootPolygons; DoShrink(distance, emptyPolygons, rootPolygons); if( distance > 0 ) { for(list<Polygon2f*>::iterator pIt =containedSolids.begin(); pIt!=containedSolids.end(); pIt++) { (*pIt)->Shrink(distance, rootPolygons, emptyPolygons); } assert(emptyPolygons.size() == 0); for(list<Polygon2f*>::iterator pIt =containedHoles.begin(); pIt!=containedHoles.end(); pIt++) { (*pIt)->DoShrink(distance, rootPolygons, emptyPolygons); } assert(emptyPolygons.size() == 0); for(list<Polygon2f*>::iterator pIt =rootPolygons.begin(); pIt!=rootPolygons.end(); ) { if( (*pIt)->TryPlaceInList(parentPolygons) ) { pIt = rootPolygons.erase(pIt); } else { pIt++; } } for(list<Polygon2f*>::iterator pIt =rootPolygons.begin(); pIt!=rootPolygons.end(); pIt++) { polygons.push_back(*pIt); } } else { for(list<Polygon2f*>::iterator pIt =containedHoles.begin(); pIt!=containedHoles.end(); pIt++) { (*pIt)->DoShrink(distance, rootPolygons, emptyPolygons); } assert(emptyPolygons.size() == 0); for(list<Polygon2f*>::iterator pIt =containedSolids.begin(); pIt!=containedSolids.end(); pIt++) { (*pIt)->DoShrink(distance, rootPolygons, emptyPolygons); } assert(emptyPolygons.size() == 0); for(list<Polygon2f*>::iterator pIt =rootPolygons.begin(); pIt!=rootPolygons.end(); ) { if( (*pIt)->TryPlaceInList(parentPolygons) ) { pIt = rootPolygons.erase(pIt); } else { pIt++; } } for(list<Polygon2f*>::iterator pIt =rootPolygons.begin(); pIt!=rootPolygons.end(); pIt++) { (*pIt)->PlaceInList(polygons); } } }
// Shrink lengths of [part1..part2] (including) // They must not exceed `right` X coordinate void CVConLine::DistributeParts(unsigned part1, unsigned part2, unsigned right) { if ((part1 > part2) || (part2 >= PartsCount)) { _ASSERTE((part1 <= part2) && (part2 < PartsCount)); return; } if (!gpSet->isCompressLongStrings) { // Do not shrink, just crop CropParts(part1, part2, right); return; } // If possible - shrink only last part // TRF_SizeFree - Spaces or horizontal frames. It does not matter, how many *real* characters we have to write // TODO: However, this may shift cells and "columns" would be not aligned if ((TextParts[part2].Flags & TRF_SizeFree) && ((TextParts[part2].PositionX + MIN_SIZEFREE_WIDTH) <= right) ) part1 = part2; if (right <= TextParts[part1].PositionX) { _ASSERTE(right > TextParts[part1].PositionX); return; } unsigned ReqWidth = (right - TextParts[part1].PositionX); unsigned FullWidth = (TextParts[part2].PositionX + TextParts[part2].TotalWidth - TextParts[part1].PositionX); // 1) ... // 2) ... // 3) ... Shrinker shrinker; shrinker.init(TextParts, part1, part2, FontWidth); shrinker.eval_compression(TextParts, part1, part2, right - TextParts[part1].PositionX); // Leftmost char coord unsigned PosX = TextParts[part1].PositionX; for (unsigned k = part1; k <= part2; k++) { VConTextPart& part = TextParts[k]; if (!part.Flags) { _ASSERTE(part.Flags); // Part must not be dropped yet! continue; } // Prepare loops TextCharType* pcf = part.CharFlags; // character flags (zero/free/normal/double) unsigned* pcw = part.CharWidth; // pointer to character width int ShrinkLeft = part.Length; unsigned NeedWidth = shrinker.data[k-part1].new_width; unsigned NewPartWidth = 0; for (unsigned c = 0; c < part.Length; c++, pcf++, pcw++) { DoShrink(*pcw, ShrinkLeft, NeedWidth, NewPartWidth); } part.TotalWidth = NewPartWidth; // Advance coord PosX += NewPartWidth; } // End of shrink (no more parts, no more methods) _ASSERTE(PosX <= right); }
// Shrink lengths of [part1..part2] (including) // They must not exceed `right` X coordinate void CVConLine::DistributeParts(uint part1, uint part2, uint right) { if ((part1 > part2) || (part2 >= PartsCount)) { _ASSERTE((part1 <= part2) && (part2 < PartsCount)); return; } // If possible - shrink only last part // TRF_SizeFree - Spaces or horizontal frames. It does not matter, how many *real* characters we may write if ((TextParts[part2].Flags & TRF_SizeFree) && ((TextParts[part2].PositionX + MIN_SIZEFREE_WIDTH) < right) ) part1 = part2; if (right <= TextParts[part1].PositionX) { _ASSERTE(right > TextParts[part1].PositionX); return; } const uint suggMul = 4, suggDiv = 5; uint FontWidth2 = (FontWidth * 2); uint FontWidth2m = (FontWidth2 * suggMul / suggDiv); uint ReqWidth = (right - TextParts[part1].PositionX); // unused? uint FullWidth = (TextParts[part2].PositionX + TextParts[part2].TotalWidth - TextParts[part1].PositionX); // 1) shrink only TCF_WidthFree chars // 2) also shrink TCF_WidthDouble (if exist) a little // 3) shrink all possibilities WARNING("Change the algorithm"); // a) count all TCF_WidthFree, TCF_WidthNormal, TCF_WidthDouble separatedly // b) so we able to find most suitable way to shrink either part with its own factor (especially useful when both CJK and single-cell chars exists) VConTextPartWidth AllWidths[TCF_WidthLast] = {}; // Count all widths for parts and check if we may do shrink with TCF_WidthFree chars only bool bHasFreeOverlaps = HasFreeOverlaps(part1, part2, right, AllWidths); uint nAllWidths = (AllWidths[TCF_WidthFree].Width + AllWidths[TCF_WidthNormal].Width + AllWidths[TCF_WidthDouble].Width); _ASSERTE(nAllWidths == FullWidth); // At the moment, they must match // What we may to shrink? if (nAllWidths <= ReqWidth) { if ((nAllWidths != ReqWidth) && !bHasFreeOverlaps) { DistributePartsFree(part1, part2, right); } else { _ASSERTE(FALSE && "Already fit, nothing to shrink"); } return; } // Only spaces and horizontal frames if (!bHasFreeOverlaps && ((AllWidths[TCF_WidthFree].MinWidth + AllWidths[TCF_WidthNormal].Width + AllWidths[TCF_WidthDouble].Width) <= ReqWidth) ) { DistributePartsFree(part1, part2, right); return; } uint nAllMin = AllWidths[TCF_WidthFree].MinWidth + AllWidths[TCF_WidthNormal].MinWidth + AllWidths[TCF_WidthDouble].MinWidth; if (!nAllMin) { _ASSERTE(nAllMin!=NULL); // Must not be zero! return; } // method and options uint nShrinkParts = (1 << TCF_WidthFree); if ((AllWidths[TCF_WidthFree].MinWidth + AllWidths[TCF_WidthNormal].Width + AllWidths[TCF_WidthDouble].Width) <= ReqWidth) { AllWidths[TCF_WidthFree].ReqWidth = ReqWidth - (AllWidths[TCF_WidthNormal].Width + AllWidths[TCF_WidthDouble].Width); } else if ((AllWidths[TCF_WidthFree].MinWidth + AllWidths[TCF_WidthNormal].Width + AllWidths[TCF_WidthDouble].MinWidth) <= ReqWidth) { // Spaces and double-space glyphs // Actually, with monospaced fonts we would have fit problems, // when double-space glyphs takes only one cell in console. // That is non-DBCS systems or UTF-8(?) on DBCS system nShrinkParts |= (1 << TCF_WidthDouble); AllWidths[TCF_WidthFree].ReqWidth = AllWidths[TCF_WidthFree].MinWidth; AllWidths[TCF_WidthDouble].ReqWidth = ReqWidth - (AllWidths[TCF_WidthNormal].Width + AllWidths[TCF_WidthFree].ReqWidth); } else { // Shrink all types nShrinkParts |= (1 << TCF_WidthNormal) | (1 << TCF_WidthDouble); // And we may apply a larger coefficient to dominating type of chars // Count each double-space glyph as 2 cells (preferred) AllWidths[TCF_WidthDouble].Count *= 2; // Count "all" cells int nAllCells = AllWidths[TCF_WidthDouble].Count + AllWidths[TCF_WidthNormal].Count + AllWidths[TCF_WidthFree].Count; // Sort types by cells used uint nMost = TCF_WidthFree; if ((AllWidths[TCF_WidthDouble].Count >= AllWidths[TCF_WidthNormal].Count) && (AllWidths[TCF_WidthDouble].Count >= AllWidths[TCF_WidthFree].Count)) nMost = TCF_WidthDouble; else if ((AllWidths[TCF_WidthNormal].Count >= AllWidths[TCF_WidthDouble].Count) && (AllWidths[TCF_WidthNormal].Count >= AllWidths[TCF_WidthFree].Count)) nMost = TCF_WidthNormal; uint nOther1 = (nMost != TCF_WidthDouble) ? TCF_WidthDouble : TCF_WidthNormal; uint nFlags = (1 << nMost) | (1 << nOther1); uint nOther2 = (!(nFlags & (1 << TCF_WidthDouble))) ? TCF_WidthDouble : (!(nFlags & (1 << TCF_WidthNormal))) ? TCF_WidthNormal : TCF_WidthFree; if (AllWidths[nOther1].Count < AllWidths[nOther2].Count) klSwap(nOther1, nOther2); uint nMostMul = (nMost == TCF_WidthDouble) ? 9 : 10; uint nMostDiv = 10; // Apply denominators AllWidths[nMost].ReqWidth = ReqWidth * AllWidths[nMost].Count * nMostMul / (nAllCells * nMostDiv); _ASSERTE(AllWidths[nMost].ReqWidth <= ReqWidth); // And prepare lesser types if ((ReqWidth - AllWidths[nMost].ReqWidth) >= (AllWidths[nOther1].Width + AllWidths[nOther2].MinWidth)) { AllWidths[nOther1].ReqWidth = AllWidths[nOther1].Width; AllWidths[nOther2].ReqWidth = ReqWidth - (AllWidths[nMost].ReqWidth + AllWidths[nOther1].ReqWidth); } else if (AllWidths[nOther1].Count > 0 && AllWidths[nOther2].Count > 0) { _ASSERTE(AllWidths[nMost].Count > 0); AllWidths[nOther1].ReqWidth = ReqWidth * AllWidths[nOther1].Count / nAllCells; AllWidths[nOther2].ReqWidth = ReqWidth - (AllWidths[nMost].ReqWidth + AllWidths[nOther1].ReqWidth); } else if (AllWidths[nOther1].Count > 0) { _ASSERTE(AllWidths[nMost].Count > 0 && AllWidths[nOther2].Count == 0); AllWidths[nOther1].ReqWidth = ReqWidth - (AllWidths[nMost].ReqWidth); _ASSERTE(AllWidths[nOther2].Width == 0); AllWidths[nOther2].ReqWidth = 0; } else { _ASSERTE(AllWidths[nMost].Count > 0 && AllWidths[nOther1].Count == 0 && AllWidths[nOther2].Count == 0); _ASSERTE(AllWidths[nOther1].Width == 0 && AllWidths[nOther2].Width == 0); AllWidths[nOther1].ReqWidth = AllWidths[nOther2].ReqWidth = 0; } // Return *char* count AllWidths[TCF_WidthDouble].Count /= 2; } // Debug validations _ASSERTE((int)AllWidths[TCF_WidthFree].ReqWidth >= 0); _ASSERTE((int)AllWidths[TCF_WidthNormal].ReqWidth >= 0); _ASSERTE((int)AllWidths[TCF_WidthDouble].ReqWidth >= 0); // *Process* the shrink // Leftmost char coord uint PosX = TextParts[part1].PositionX; for (uint k = part1; k <= part2; k++) { VConTextPart& part = TextParts[k]; if (!part.Flags) { _ASSERTE(part.Flags); // Part must not be dropped yet! continue; } // Update new leftmost coord for this part part.PositionX = PosX; // Prepare loops TextCharType* pcf = part.CharFlags; // character flags (zero/free/normal/double) uint* pcw = part.CharWidth; // pointer to character width // Run part shrink part.TotalWidth = 0; if ((part.Flags & TRF_SizeFree) && (nShrinkParts & (1 << TCF_WidthFree))) { VConTextPartWidth& aw = AllWidths[TCF_WidthFree]; int iShrinkLeft = part.Length; _ASSERTE(AllWidths[TCF_WidthFree].Count>0); uint partReqWidth = (AllWidths[TCF_WidthFree].Count > 0) ? (aw.ReqWidth / AllWidths[TCF_WidthFree].Count) : 0; for (uint c = 0; c < part.Length; c++, pcf++, pcw++) { _ASSERTE(*pcf == TCF_WidthFree); DoShrink(*pcw, iShrinkLeft, partReqWidth, part.TotalWidth); } } else { for (uint c = 0; c < part.Length; c++, pcf++, pcw++) { if (nShrinkParts & (1 << *pcf)) { VConTextPartWidth& aw = AllWidths[*pcf]; DoShrink(*pcw, aw.Count, aw.ReqWidth, part.TotalWidth); } else { part.TotalWidth += *pcw; } } } PosX += part.TotalWidth; } // End of shrink (no more parts, no more methods) _ASSERTE(PosX <= right); }