| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #ifdef WNT |
| #include <tools/svwin.h> |
| #include <svsys.h> |
| #endif |
| |
| #include <tools/debug.hxx> |
| #include <sallayout.hxx> |
| |
| #include <preextstl.h> |
| #include <graphite/GrClient.h> |
| #include <graphite/Segment.h> |
| #include <postextstl.h> |
| |
| #include <rtl/ustring.hxx> |
| #include <graphite_layout.hxx> |
| #include <graphite_cache.hxx> |
| |
| #include "graphite_textsrc.hxx" |
| |
| GrSegRecord::GrSegRecord(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl) |
| : m_rope(rope), m_text(textSrc), m_seg(seg), m_nextKey(NULL), |
| m_fontScale(0.0f), mbIsRtl(bIsRtl), m_lockCount(0) |
| { |
| m_pStr = textSrc->getLayoutArgs().mpStr + seg->startCharacter(); |
| m_startChar = seg->startCharacter(); |
| } |
| |
| GrSegRecord::~GrSegRecord() |
| { |
| clear(); |
| } |
| |
| void GrSegRecord::reuse(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl) |
| { |
| clear(); |
| mnWidth = 0; |
| m_rope = rope; |
| m_text = textSrc; |
| m_seg = seg; |
| m_nextKey = NULL; |
| m_pStr = textSrc->getLayoutArgs().mpStr + seg->startCharacter(); |
| m_startChar = seg->startCharacter(); |
| mbIsRtl = bIsRtl; |
| } |
| |
| void GrSegRecord::clearVectors() |
| { |
| mvGlyphs.clear(); |
| mvCharDxs.clear(); |
| mvChar2BaseGlyph.clear(); |
| mvGlyph2Char.clear(); |
| } |
| |
| void GrSegRecord::clear() |
| { |
| #ifdef GR_DEBUG_TEXT |
| if (m_lockCount != 0) |
| OutputDebugString("GrSegRecord locked!"); |
| #endif |
| clearVectors(); |
| delete m_rope; |
| delete m_seg; |
| delete m_text; |
| m_rope = NULL; |
| m_seg = NULL; |
| m_text = NULL; |
| m_fontScale = 0.0f; |
| m_lockCount = 0; |
| } |
| |
| GrSegRecord * GraphiteSegmentCache::cacheSegment(TextSourceAdaptor * adapter, gr::Segment * seg, bool bIsRtl) |
| { |
| GrSegRecord * record = NULL; |
| // We keep a record of the oldest key and the last key added |
| // when the next key is added, the record for the prevKey's m_nextKey field |
| // is updated to the newest key so that m_oldestKey can be updated to the |
| // next oldest key when the record for m_oldestKey is deleted |
| if (m_segMap.size() > m_nSegCacheSize) |
| { |
| GraphiteSegMap::iterator oldestPair = m_segMap.find(reinterpret_cast<long>(m_oldestKey)); |
| // oldest record may no longer exist if a buffer was changed |
| if (oldestPair != m_segMap.end()) |
| { |
| record = oldestPair->second; |
| m_segMap.erase(reinterpret_cast<long>(m_oldestKey)); |
| GrRMEntry range = m_ropeMap.equal_range((*(record->m_rope)).hashCode()); |
| while (range.first != range.second) |
| { |
| if (range.first->second == record) |
| { |
| m_ropeMap.erase(range.first); |
| break; |
| } |
| ++range.first; |
| } |
| m_oldestKey = record->m_nextKey; |
| // record will be reused, so don't delete |
| } |
| } |
| |
| |
| // const int seg_char_limit = min(adapter->maLayoutArgs().mnLength, |
| // adapter->maLayoutArgs().mnEndCharPos |
| // + GraphiteLayout::EXTRA_CONTEXT_LENGTH); |
| // if (seg->stopCharacter() - seg->startCharacter() <= 0) |
| // OutputDebugString("Invalid seg indices\n"); |
| rtl::OUString * pRope = new rtl::OUString(adapter->getLayoutArgs().mpStr + seg->startCharacter(), |
| seg->stopCharacter() - seg->startCharacter()); |
| if (!pRope) return NULL; |
| bool reuse = false; |
| if (record) |
| record->reuse(pRope, adapter, seg, bIsRtl); |
| else |
| record = new GrSegRecord(pRope, adapter, seg, bIsRtl); |
| if (!record) |
| { |
| delete pRope; |
| return NULL; |
| } |
| GraphiteSegMap::iterator iMap = |
| m_segMap.find(reinterpret_cast<long>(record->m_pStr)); |
| if (iMap != m_segMap.end()) |
| { |
| // the buffer has changed, so the old cached Segment is useless |
| reuse = true; |
| GrSegRecord * found = iMap->second; |
| // Note: we reuse the old next key to avoid breaking our history |
| // chain. This means it will be prematurely deleted, but this is |
| // unlikely to happen very often. |
| record->m_nextKey = found->m_nextKey; |
| // overwrite the old record |
| m_segMap[reinterpret_cast<long>(record->m_pStr)] = record; |
| // erase the old rope key and save the new one |
| GrRMEntry range = m_ropeMap.equal_range((*(found->m_rope)).hashCode()); |
| while (range.first != range.second) |
| { |
| if (range.first->second == found) |
| { |
| m_ropeMap.erase(range.first); |
| break; |
| } |
| ++range.first; |
| } |
| GraphiteRopeMap::value_type mapEntry(record->m_rope->hashCode(), record); |
| m_ropeMap.insert(mapEntry); |
| // remove the old record |
| delete found; |
| record->m_lockCount++; |
| return record; |
| } |
| m_segMap[reinterpret_cast<long>(record->m_pStr)] = record; |
| GraphiteRopeMap::value_type mapEntry((*(record->m_rope)).hashCode(), record); |
| m_ropeMap.insert(mapEntry); |
| |
| if (m_oldestKey == NULL) |
| { |
| m_oldestKey = record->m_pStr; |
| m_prevKey = record->m_pStr; |
| } |
| else if (reuse == false) |
| { |
| DBG_ASSERT(m_segMap.count(reinterpret_cast<long>(m_prevKey)), |
| "Previous key got lost somehow!"); |
| m_segMap.find(reinterpret_cast<long>(m_prevKey)) |
| ->second->m_nextKey = record->m_pStr; |
| m_prevKey = record->m_pStr; |
| } |
| record->m_lockCount++; |
| return record; |
| } |