Example #1
0
void
AudioStream::GetTimeStretched(AudioBufferWriter& aWriter)
{
  mMonitor.AssertCurrentThreadOwns();

  // We need to call the non-locking version, because we already have the lock.
  if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
    return;
  }

  uint32_t toPopFrames =
    ceil(aWriter.Available() * mAudioClock.GetPlaybackRate());

  while (mTimeStretcher->numSamples() < aWriter.Available()) {
    UniquePtr<Chunk> c = mDataSource.PopFrames(toPopFrames);
    if (c->Frames() == 0) {
      break;
    }
    MOZ_ASSERT(c->Frames() <= toPopFrames);
    if (IsValidAudioFormat(c.get())) {
      mTimeStretcher->putSamples(c->Data(), c->Frames());
    } else {
      // Write silence if invalid format.
      AutoTArray<AudioDataValue, 1000> buf;
      buf.SetLength(mOutChannels * c->Frames());
      memset(buf.Elements(), 0, buf.Length() * sizeof(AudioDataValue));
      mTimeStretcher->putSamples(buf.Elements(), c->Frames());
    }
  }

  auto timeStretcher = mTimeStretcher;
  aWriter.Write([timeStretcher] (AudioDataValue* aPtr, uint32_t aFrames) {
    return timeStretcher->receiveSamples(aPtr, aFrames);
  }, aWriter.Available());
}
static void
CopyChunkToBlock(AudioChunk& aInput, AudioBlock *aBlock,
                 uint32_t aOffsetInBlock)
{
  uint32_t blockChannels = aBlock->ChannelCount();
  AutoTArray<const T*,2> channels;
  if (aInput.IsNull()) {
    channels.SetLength(blockChannels);
    PodZero(channels.Elements(), blockChannels);
  } else {
    const nsTArray<const T*>& inputChannels = aInput.ChannelData<T>();
    channels.SetLength(inputChannels.Length());
    PodCopy(channels.Elements(), inputChannels.Elements(), channels.Length());
    if (channels.Length() != blockChannels) {
      // We only need to upmix here because aBlock's channel count has been
      // chosen to be a superset of the channel count of every chunk.
      AudioChannelsUpMix(&channels, blockChannels, static_cast<T*>(nullptr));
    }
  }

  for (uint32_t c = 0; c < blockChannels; ++c) {
    float* outputData = aBlock->ChannelFloatsForWrite(c) + aOffsetInBlock;
    if (channels[c]) {
      ConvertAudioSamplesWithScale(channels[c], outputData, aInput.GetDuration(), aInput.mVolume);
    } else {
      PodZero(outputData, aInput.GetDuration());
    }
  }
}
Example #3
0
void
nsShmImage::Put(const mozilla::LayoutDeviceIntRegion& aRegion)
{
  AutoTArray<xcb_rectangle_t, 32> xrects;
  xrects.SetCapacity(aRegion.GetNumRects());

  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
    const mozilla::LayoutDeviceIntRect &r = iter.Get();
    xcb_rectangle_t xrect = { (short)r.x, (short)r.y, (unsigned short)r.width, (unsigned short)r.height };
    xrects.AppendElement(xrect);
  }

  if (!mGC) {
    mGC = xcb_generate_id(mConnection);
    xcb_create_gc(mConnection, mGC, mWindow, 0, nullptr);
  }

  xcb_set_clip_rectangles(mConnection, XCB_CLIP_ORDERING_YX_BANDED, mGC, 0, 0,
                          xrects.Length(), xrects.Elements());

  if (mPixmap != XCB_NONE) {
    mLastRequest = xcb_copy_area_checked(mConnection, mPixmap, mWindow, mGC,
                                         0, 0, 0, 0, mSize.width, mSize.height);
  } else {
    mLastRequest = xcb_shm_put_image_checked(mConnection, mWindow, mGC,
                                             mSize.width, mSize.height,
                                             0, 0, mSize.width, mSize.height,
                                             0, 0, mDepth,
                                             XCB_IMAGE_FORMAT_Z_PIXMAP, 0,
                                             mShmSeg, 0);
  }

  xcb_flush(mConnection);
}
Example #4
0
void
AudioStream::GetTimeStretched(AudioBufferWriter& aWriter)
{
  mMonitor.AssertCurrentThreadOwns();

  // We need to call the non-locking version, because we already have the lock.
  if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
    return;
  }

  uint32_t toPopFrames =
    ceil(aWriter.Available() * mAudioClock.GetPlaybackRate());

  while (mTimeStretcher->numSamples() < aWriter.Available()) {
    UniquePtr<Chunk> c = mDataSource.PopFrames(toPopFrames);
    if (c->Frames() == 0) {
      break;
    }
    MOZ_ASSERT(c->Frames() <= toPopFrames);
    if (IsValidAudioFormat(c.get())) {
      mTimeStretcher->putSamples(c->Data(), c->Frames());
    } else {
      // Write silence if invalid format.
      AutoTArray<AudioDataValue, 1000> buf;
      auto size = CheckedUint32(mOutChannels) * c->Frames();
      if (!size.isValid()) {
        // The overflow should not happen in normal case.
        LOGW("Invalid member data: %d channels, %d frames", mOutChannels, c->Frames());
        return;
      }
      buf.SetLength(size.value());
      size = size * sizeof(AudioDataValue);
      if (!size.isValid()) {
        LOGW("The required memory size is too large.");
        return;
      }
      memset(buf.Elements(), 0, size.value());
      mTimeStretcher->putSamples(buf.Elements(), c->Frames());
    }
  }

  auto timeStretcher = mTimeStretcher;
  aWriter.Write([timeStretcher] (AudioDataValue* aPtr, uint32_t aFrames) {
    return timeStretcher->receiveSamples(aPtr, aFrames);
  }, aWriter.Available());
}
  // Read audio data in aChunk, resample them if needed,
  // and then send the result to OMX input buffer (or buffers if one buffer is not enough).
  // aSamplesRead will be the number of samples that have been read from aChunk.
  BufferState ReadChunk(AudioChunk& aChunk, size_t* aSamplesRead)
  {
    size_t chunkSamples = aChunk.GetDuration();
    size_t bytesToCopy = chunkSamples * mOMXAEncoder.mResamplingRatio
                         * mOMXAEncoder.mChannels * sizeof(AudioDataValue);
    size_t bytesCopied = 0;
    if (bytesToCopy <= AvailableSize()) {
      if (aChunk.IsNull()) {
        bytesCopied = SendSilenceToBuffer(chunkSamples);
      } else {
        bytesCopied = SendChunkToBuffer(aChunk, chunkSamples);
      }
      UpdateAfterSendChunk(chunkSamples, bytesCopied, aSamplesRead);
    } else {
      // Interleave data to a temporary buffer.
      AutoTArray<AudioDataValue, 9600> pcm;
      pcm.SetLength(bytesToCopy);
      AudioDataValue* interleavedSource = pcm.Elements();
      AudioTrackEncoder::InterleaveTrackData(aChunk, chunkSamples,
                                             mOMXAEncoder.mChannels,
                                             interleavedSource);

      // When the data size of chunk is larger than the buffer capacity,
      // we split it into sub-chunks to fill up buffers.
      size_t subChunkSamples = 0;
      while(GetNextSubChunk(bytesToCopy, subChunkSamples)) {
        // To avoid enqueueing an empty buffer, we follow the order that
        // clear up buffer first, then create one, send data to it in the end.
        if (!IsEmpty()) {
          // Submit the filled-up buffer and request a new buffer.
          status_t result = Enqueue(mOMXAEncoder.mTimestamp,
                                    mInputFlags & ~OMXCodecWrapper::BUFFER_EOS);
          if (result != OK) {
            return BUFFER_FAIL;
          }

          result = Dequeue();
          if (result == -EAGAIN) {
            return WAIT_FOR_NEW_BUFFER;
          }
          if (result != OK) {
            return BUFFER_FAIL;
          }
        }
        if (aChunk.IsNull()) {
          bytesCopied = SendSilenceToBuffer(subChunkSamples);
        } else {
          bytesCopied = SendInterleavedSubChunkToBuffer(interleavedSource, subChunkSamples);
        }
        UpdateAfterSendChunk(subChunkSamples, bytesCopied, aSamplesRead);
        // Move to the position where samples are not yet send to the buffer.
        interleavedSource += subChunkSamples * mOMXAEncoder.mChannels;
      }
    }
    return BUFFER_OK;
  }
Example #6
0
void
NS_GetComplexLineBreaks(const char16_t* aText, uint32_t aLength,
                        uint8_t* aBreakBefore)
{
  NS_ASSERTION(aText, "aText shouldn't be null"); 

  int outItems = 0;
  HRESULT result;
  AutoTArray<SCRIPT_ITEM, 64> items;
  char16ptr_t text = aText;

  memset(aBreakBefore, false, aLength);

  if (!items.AppendElements(64))
    return;

  do {
    result = ScriptItemize(text, aLength, items.Length(), nullptr, nullptr,
                           items.Elements(), &outItems);

    if (result == E_OUTOFMEMORY) {
      if (!items.AppendElements(items.Length()))
        return;
    }
  } while (result == E_OUTOFMEMORY);

  for (int iItem = 0; iItem < outItems; ++iItem)  {
    uint32_t endOffset = (iItem + 1 == outItems ? aLength : items[iItem + 1].iCharPos);
    uint32_t startOffset = items[iItem].iCharPos;
    AutoTArray<SCRIPT_LOGATTR, 64> sla;
    
    if (!sla.AppendElements(endOffset - startOffset))
      return;

    if (ScriptBreak(text + startOffset, endOffset - startOffset,
                    &items[iItem].a,  sla.Elements()) < 0) 
      return;

    for (uint32_t j=0; j+startOffset < endOffset; ++j) {
       aBreakBefore[j+startOffset] = sla[j].fSoftBreak;
    }
  }
}
Example #7
0
typename EnableIf<IsSame<T, float>::value, void>::Type
WriteDumpFileHelper(T* aInput, size_t aSamples, FILE* aFile) {
  AutoTArray<uint8_t, 1024*2> buf;
  buf.SetLength(aSamples*2);
  uint8_t* output = buf.Elements();
  for (uint32_t i = 0; i < aSamples; ++i) {
    SetUint16LE(output + i*2, int16_t(aInput[i]*32767.0f));
  }
  fwrite(output, 2, aSamples, aFile);
  fflush(aFile);
}
 // Interleave chunk data and send it to buffer,
 // and return the copied bytes number of audio data.
 size_t SendChunkToBuffer(AudioChunk& aSource, size_t aSamplesNum)
 {
   AudioDataValue* dst = reinterpret_cast<AudioDataValue*>(GetPointer());
   size_t bytesToCopy = aSamplesNum * mOMXAEncoder.mResamplingRatio
                        * mOMXAEncoder.mChannels * sizeof(AudioDataValue);
   uint32_t dstSamplesCopied = aSamplesNum;
   if (mOMXAEncoder.mResampler) {
     AutoTArray<AudioDataValue, 9600> pcm;
     pcm.SetLength(bytesToCopy);
     AudioTrackEncoder::InterleaveTrackData(aSource, aSamplesNum,
                                            mOMXAEncoder.mChannels,
                                            pcm.Elements());
     int16_t* tempSource = reinterpret_cast<int16_t*>(pcm.Elements());
     speex_resampler_process_interleaved_int(mOMXAEncoder.mResampler, tempSource,
                                             &aSamplesNum, dst,
                                             &dstSamplesCopied);
   } else {
     AudioTrackEncoder::InterleaveTrackData(aSource, aSamplesNum,
                                            mOMXAEncoder.mChannels, dst);
   }
   return dstSamplesCopied * mOMXAEncoder.mChannels * sizeof(AudioDataValue);
 }
Example #9
0
void
DOMMatrixReadOnly::ToFloat64Array(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv) const
{
  AutoTArray<double, 16> arr;
  arr.SetLength(16);
  GetDataFromMatrix(this, arr.Elements());
  JS::Rooted<JS::Value> value(aCx);
  if (!ToJSValue(aCx, TypedArrayCreator<Float64Array>(arr), &value)) {
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    return;
  }
  aResult.set(&value.toObject());
}
Example #10
0
void
NS_GetComplexLineBreaks(const char16_t* aText, uint32_t aLength,
                        uint8_t* aBreakBefore)
{
  NS_ASSERTION(aText, "aText shouldn't be null");

  memset(aBreakBefore, false, aLength * sizeof(uint8_t));

  AutoTArray<PangoLogAttr, 2000> attrBuffer;
  if (!attrBuffer.AppendElements(aLength + 1))
    return;

  NS_ConvertUTF16toUTF8 aUTF8(aText, aLength);

  const gchar* p = aUTF8.Data();
  const gchar* end = p + aUTF8.Length();
  uint32_t     u16Offset = 0;

  static PangoLanguage* language = pango_language_from_string("en");

  while (p < end)
  {
    PangoLogAttr* attr = attrBuffer.Elements();
    pango_get_log_attrs(p, end - p, -1, language, attr, attrBuffer.Length());

    while (p < end)
    {
      aBreakBefore[u16Offset] = attr->is_line_break;
      if (NS_IS_LOW_SURROGATE(aText[u16Offset]))
        aBreakBefore[++u16Offset] = false; // Skip high surrogate
      ++u16Offset;

      bool err;
      uint32_t ch = UTF8CharEnumerator::NextChar(&p, end, &err);
      ++attr;

      if (ch == 0 || err) {
        // pango_break (pango 1.16.2) only analyses text before the
        // first NUL (but sets one extra attr). Workaround loop to call
        // pango_break again to analyse after the NUL is done somewhere else
        // (gfx/thebes/gfxFontconfigFonts.cpp: SetupClusterBoundaries()).
        // So, we do the same here for pango_get_log_attrs.
        break;
      }
    }
  }
}
void
AudioNodeStream::UpMixDownMixChunk(const AudioBlock* aChunk,
                                   uint32_t aOutputChannelCount,
                                   nsTArray<const float*>& aOutputChannels,
                                   DownmixBufferType& aDownmixBuffer)
{
  for (uint32_t i = 0; i < aChunk->ChannelCount(); i++) {
    aOutputChannels.AppendElement(static_cast<const float*>(aChunk->mChannelData[i]));
  }
  if (aOutputChannels.Length() < aOutputChannelCount) {
    if (mChannelInterpretation == ChannelInterpretation::Speakers) {
      AudioChannelsUpMix<float>(&aOutputChannels, aOutputChannelCount, nullptr);
      NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
                   "We called GetAudioChannelsSuperset to avoid this");
    } else {
      // Fill up the remaining aOutputChannels by zeros
      for (uint32_t j = aOutputChannels.Length(); j < aOutputChannelCount; ++j) {
        aOutputChannels.AppendElement(nullptr);
      }
    }
  } else if (aOutputChannels.Length() > aOutputChannelCount) {
    if (mChannelInterpretation == ChannelInterpretation::Speakers) {
      AutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannels;
      outputChannels.SetLength(aOutputChannelCount);
      aDownmixBuffer.SetLength(aOutputChannelCount * WEBAUDIO_BLOCK_SIZE);
      for (uint32_t j = 0; j < aOutputChannelCount; ++j) {
        outputChannels[j] = &aDownmixBuffer[j * WEBAUDIO_BLOCK_SIZE];
      }

      AudioChannelsDownMix(aOutputChannels, outputChannels.Elements(),
                           aOutputChannelCount, WEBAUDIO_BLOCK_SIZE);

      aOutputChannels.SetLength(aOutputChannelCount);
      for (uint32_t j = 0; j < aOutputChannels.Length(); ++j) {
        aOutputChannels[j] = outputChannels[j];
      }
    } else {
      // Drop the remaining aOutputChannels
      aOutputChannels.RemoveElementsAt(aOutputChannelCount,
        aOutputChannels.Length() - aOutputChannelCount);
    }
  }
}
Example #12
0
NS_IMETHODIMP
xpcAccessibleTable::GetSelectedRowIndices(uint32_t* aRowsArraySize,
                                          int32_t** aRowsArray) {
  NS_ENSURE_ARG_POINTER(aRowsArraySize);
  *aRowsArraySize = 0;

  NS_ENSURE_ARG_POINTER(aRowsArray);
  *aRowsArray = 0;

  if (!Intl()) return NS_ERROR_FAILURE;

  AutoTArray<uint32_t, XPC_TABLE_DEFAULT_SIZE> rowsArray;
  Intl()->SelectedRowIndices(&rowsArray);

  *aRowsArraySize = rowsArray.Length();
  *aRowsArray =
      static_cast<int32_t*>(moz_xmalloc(*aRowsArraySize * sizeof(int32_t)));
  memcpy(*aRowsArray, rowsArray.Elements(), *aRowsArraySize * sizeof(int32_t));

  return NS_OK;
}
Example #13
0
void
nsShmImage::Put(const mozilla::LayoutDeviceIntRegion& aRegion)
{
  AutoTArray<xcb_rectangle_t, 32> xrects;
  xrects.SetCapacity(aRegion.GetNumRects());

  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
    const mozilla::LayoutDeviceIntRect &r = iter.Get();
    xcb_rectangle_t xrect = { (short)r.x, (short)r.y, (unsigned short)r.width, (unsigned short)r.height };
    xrects.AppendElement(xrect);
  }

  if (!mGC) {
    mGC = xcb_generate_id(mConnection);
    xcb_create_gc(mConnection, mGC, mWindow, 0, nullptr);
  }

  xcb_set_clip_rectangles(mConnection, XCB_CLIP_ORDERING_YX_BANDED, mGC, 0, 0,
                          xrects.Length(), xrects.Elements());

  if (mPixmap != XCB_NONE) {
    mPutRequest = xcb_copy_area_checked(mConnection, mPixmap, mWindow, mGC,
                                        0, 0, 0, 0, mSize.width, mSize.height);
  } else {
    mPutRequest = xcb_shm_put_image_checked(mConnection, mWindow, mGC,
                                            mSize.width, mSize.height,
                                            0, 0, mSize.width, mSize.height,
                                            0, 0, mDepth,
                                            XCB_IMAGE_FORMAT_Z_PIXMAP, 0,
                                            mShmSeg, 0);
  }

  // Send a request that returns a response so that we don't have to start a
  // sync in nsShmImage::CreateDrawTarget to retrieve the result of mPutRequest.
  mSyncRequest = xcb_get_input_focus(mConnection);
  mRequestPending = true;

  xcb_flush(mConnection);
}
Example #14
0
// performs a locale sensitive date formatting operation on the struct tm parameter
nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale, 
                                           const nsDateFormatSelector  dateFormatSelector, 
                                           const nsTimeFormatSelector timeFormatSelector, 
                                           const struct tm*  tmTime, 
                                           nsAString& stringOut)
{
  nsresult res = NS_OK;

  // set up locale data
  (void) Initialize(locale);
  
  // return, nothing to format
  if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) {
    stringOut.Truncate();
    return NS_OK;
  }

  NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly");
  NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly");
  NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly");
  NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly");
  NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly");
  NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly");

  // Got the locale for the formatter:
  CFLocaleRef formatterLocale;
  if (!locale) {
    formatterLocale = CFLocaleCopyCurrent();
  } else {
    CFStringRef localeStr = CFStringCreateWithCharacters(nullptr,
                                                         reinterpret_cast<const UniChar*>(mLocale.get()),
                                                         mLocale.Length());
    formatterLocale = CFLocaleCreate(nullptr, localeStr);
    CFRelease(localeStr);
  }

  // Get the date style for the formatter:  
  CFDateFormatterStyle dateStyle;
  switch (dateFormatSelector) {
    case kDateFormatLong:
      dateStyle = kCFDateFormatterLongStyle;
      break;
    case kDateFormatShort:
      dateStyle = kCFDateFormatterShortStyle;
      break;
    case kDateFormatYearMonth:
    case kDateFormatWeekday:
      dateStyle = kCFDateFormatterNoStyle; // formats handled below
      break;
    case kDateFormatNone:
      dateStyle = kCFDateFormatterNoStyle;
      break;
    default:
      NS_ERROR("Unknown nsDateFormatSelector");
      res = NS_ERROR_FAILURE;
      dateStyle = kCFDateFormatterNoStyle;
  }
  
  // Get the time style for the formatter:
  CFDateFormatterStyle timeStyle;
  switch (timeFormatSelector) {
    case kTimeFormatSeconds:
    case kTimeFormatSecondsForce24Hour: // 24 hour part fixed below
      timeStyle = kCFDateFormatterMediumStyle;
      break;
    case kTimeFormatNoSeconds:
    case kTimeFormatNoSecondsForce24Hour: // 24 hour part fixed below
      timeStyle = kCFDateFormatterShortStyle;
      break;
    case kTimeFormatNone:
      timeStyle = kCFDateFormatterNoStyle;
      break;
    default:
      NS_ERROR("Unknown nsTimeFormatSelector");
      res = NS_ERROR_FAILURE;
      timeStyle = kCFDateFormatterNoStyle;
  }
  
  // Create the formatter and fix up its formatting as necessary:
  CFDateFormatterRef formatter =
    CFDateFormatterCreate(nullptr, formatterLocale, dateStyle, timeStyle);
  
  CFRelease(formatterLocale);
  
  if (dateFormatSelector == kDateFormatYearMonth ||
      dateFormatSelector == kDateFormatWeekday) {
    CFStringRef dateFormat =
      dateFormatSelector == kDateFormatYearMonth ? CFSTR("yyyy/MM ") : CFSTR("EEE ");
    
    CFStringRef oldFormat = CFDateFormatterGetFormat(formatter);
    CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat);
    CFStringInsert(newFormat, 0, dateFormat);
    CFDateFormatterSetFormat(formatter, newFormat);
    CFRelease(newFormat); // note we don't own oldFormat
  }
  
  if (timeFormatSelector == kTimeFormatSecondsForce24Hour ||
      timeFormatSelector == kTimeFormatNoSecondsForce24Hour) {
    // Replace "h" with "H", and remove "a":
    CFStringRef oldFormat = CFDateFormatterGetFormat(formatter);
    CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat);
    CFIndex replaceCount = CFStringFindAndReplace(newFormat,
                                                  CFSTR("h"), CFSTR("H"),
                                                  CFRangeMake(0, CFStringGetLength(newFormat)),	
                                                  0);
    NS_ASSERTION(replaceCount <= 2, "Unexpected number of \"h\" occurrences");
    replaceCount = CFStringFindAndReplace(newFormat,
                                          CFSTR("a"), CFSTR(""),
                                          CFRangeMake(0, CFStringGetLength(newFormat)),	
                                          0);
    NS_ASSERTION(replaceCount <= 1, "Unexpected number of \"a\" occurrences");
    CFDateFormatterSetFormat(formatter, newFormat);
    CFRelease(newFormat); // note we don't own oldFormat
  }
  
  // Now get the formatted date:
  CFGregorianDate date;
  date.second = tmTime->tm_sec;
  date.minute = tmTime->tm_min;
  date.hour = tmTime->tm_hour;
  date.day = tmTime->tm_mday;      // Mac is 1-based, tm is 1-based
  date.month = tmTime->tm_mon + 1; // Mac is 1-based, tm is 0-based
  date.year = tmTime->tm_year + 1900;

  CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); // tmTime is in local time
  CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
  CFRelease(timeZone);

  CFStringRef formattedDate = CFDateFormatterCreateStringWithAbsoluteTime(nullptr,
                                                                          formatter,
                                                                          absTime);

  CFIndex stringLen = CFStringGetLength(formattedDate);

  AutoTArray<UniChar, 256> stringBuffer;
  stringBuffer.SetLength(stringLen + 1);
  CFStringGetCharacters(formattedDate, CFRangeMake(0, stringLen), stringBuffer.Elements());
  stringOut.Assign(reinterpret_cast<char16_t*>(stringBuffer.Elements()), stringLen);

  CFRelease(formattedDate);
  CFRelease(formatter);

  return res;
}
nsresult
gfxGraphiteShaper::SetGlyphsFromSegment(DrawTarget      *aDrawTarget,
                                        gfxShapedText   *aShapedText,
                                        uint32_t         aOffset,
                                        uint32_t         aLength,
                                        const char16_t *aText,
                                        gr_segment      *aSegment)
{
    int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
    bool rtl = aShapedText->IsRightToLeft();

    uint32_t glyphCount = gr_seg_n_slots(aSegment);

    // identify clusters; graphite may have reordered/expanded/ligated glyphs.
    AutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
    AutoTArray<uint16_t,SMALL_GLYPH_RUN> gids;
    AutoTArray<float,SMALL_GLYPH_RUN> xLocs;
    AutoTArray<float,SMALL_GLYPH_RUN> yLocs;

    if (!clusters.SetLength(aLength, fallible) ||
        !gids.SetLength(glyphCount, fallible) ||
        !xLocs.SetLength(glyphCount, fallible) ||
        !yLocs.SetLength(glyphCount, fallible))
    {
        return NS_ERROR_OUT_OF_MEMORY;
    }

    // walk through the glyph slots and check which original character
    // each is associated with
    uint32_t gIndex = 0; // glyph slot index
    uint32_t cIndex = 0; // current cluster index
    for (const gr_slot *slot = gr_seg_first_slot(aSegment);
         slot != nullptr;
         slot = gr_slot_next_in_segment(slot), gIndex++)
    {
        uint32_t before =
            gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot)));
        uint32_t after =
            gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot)));
        gids[gIndex] = gr_slot_gid(slot);
        xLocs[gIndex] = gr_slot_origin_X(slot);
        yLocs[gIndex] = gr_slot_origin_Y(slot);

        // if this glyph has a "before" character index that precedes the
        // current cluster's char index, we need to merge preceding
        // clusters until it gets included
        while (before < clusters[cIndex].baseChar && cIndex > 0) {
            clusters[cIndex-1].nChars += clusters[cIndex].nChars;
            clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs;
            --cIndex;
        }

        // if there's a gap between the current cluster's base character and
        // this glyph's, extend the cluster to include the intervening chars
        if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
            before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
        {
            NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word");
            Cluster& c = clusters[cIndex + 1];
            c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
            c.nChars = before - c.baseChar;
            c.baseGlyph = gIndex;
            c.nGlyphs = 0;
            ++cIndex;
        }

        // increment cluster's glyph count to include current slot
        NS_ASSERTION(cIndex < aLength, "cIndex beyond word length");
        ++clusters[cIndex].nGlyphs;

        // bump |after| index if it falls in the middle of a surrogate pair
        if (NS_IS_HIGH_SURROGATE(aText[after]) && after < aLength - 1 &&
            NS_IS_LOW_SURROGATE(aText[after + 1])) {
            after++;
        }
        // extend cluster if necessary to reach the glyph's "after" index
        if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
            clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
        }
    }

    bool roundX, roundY;
    GetRoundOffsetsToPixels(aDrawTarget, &roundX, &roundY);

    gfxShapedText::CompressedGlyph *charGlyphs =
        aShapedText->GetCharacterGlyphs() + aOffset;

    // now put glyphs into the textrun, one cluster at a time
    for (uint32_t i = 0; i <= cIndex; ++i) {
        const Cluster& c = clusters[i];

        float adv; // total advance of the cluster
        if (rtl) {
            if (i == 0) {
                adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
            } else {
                adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph];
            }
        } else {
            if (i == cIndex) {
                adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
            } else {
                adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
            }
        }

        // Check for default-ignorable char that didn't get filtered, combined,
        // etc by the shaping process, and skip it.
        uint32_t offs = c.baseChar;
        NS_ASSERTION(offs < aLength, "unexpected offset");
        if (c.nGlyphs == 1 && c.nChars == 1 &&
            aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
            continue;
        }

        uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits :
                                       NSToIntRound(adv * dev2appUnits);
        if (c.nGlyphs == 1 &&
            gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
            gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
            charGlyphs[offs].IsClusterStart() &&
            yLocs[c.baseGlyph] == 0)
        {
            charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]);
        } else {
            // not a one-to-one mapping with simple metrics: use DetailedGlyph
            AutoTArray<gfxShapedText::DetailedGlyph,8> details;
            float clusterLoc;
            for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
                gfxShapedText::DetailedGlyph* d = details.AppendElement();
                d->mGlyphID = gids[j];
                d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits :
                              -yLocs[j] * dev2appUnits;
                if (j == c.baseGlyph) {
                    d->mXOffset = 0;
                    d->mAdvance = appAdvance;
                    clusterLoc = xLocs[j];
                } else {
                    float dx = rtl ? (xLocs[j] - clusterLoc) :
                                     (xLocs[j] - clusterLoc - adv);
                    d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits :
                                           dx * dev2appUnits;
                    d->mAdvance = 0;
                }
            }
            gfxShapedText::CompressedGlyph g;
            g.SetComplex(charGlyphs[offs].IsClusterStart(),
                         true, details.Length());
            aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
        }

        for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
            NS_ASSERTION(j < aLength, "unexpected offset");
            gfxShapedText::CompressedGlyph &g = charGlyphs[j];
            NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
            g.SetComplex(g.IsClusterStart(), false, 0);
        }
    }

    return NS_OK;
}
Example #16
0
nsresult
GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
{
    PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);

    // attempt this once, if errors occur leave a blank cmap
    if (mCharacterMap) {
        return NS_OK;
    }

    // skip non-SFNT fonts completely
    if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE && 
        mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
        mFontType != GFX_FONT_TYPE_TRUETYPE) 
    {
        mCharacterMap = new gfxCharacterMap();
        mCharacterMap->mBuildOnTheFly = true;
        return NS_ERROR_FAILURE;
    }

    RefPtr<gfxCharacterMap> charmap;
    nsresult rv;
    bool unicodeFont = false, symbolFont = false;

    if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
                                                        mUVSOffset,
                                                        symbolFont))) {
        mSymbolFont = symbolFont;
        rv = NS_OK;
    } else {
        uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
        charmap = new gfxCharacterMap();
        AutoTArray<uint8_t, 16384> cmap;
        rv = CopyFontTable(kCMAP, cmap);

        if (NS_SUCCEEDED(rv)) {
            rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
                                        *charmap, mUVSOffset,
                                        unicodeFont, symbolFont);
        }
        mSymbolFont = symbolFont;
    }

    mHasCmapTable = NS_SUCCEEDED(rv);
    if (mHasCmapTable) {
        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
        mCharacterMap = pfl->FindCharMap(charmap);
    } else {
        // if error occurred, initialize to null cmap
        mCharacterMap = new gfxCharacterMap();
        // For fonts where we failed to read the character map,
        // we can take a slow path to look up glyphs character by character
        mCharacterMap->mBuildOnTheFly = true;
    }

    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                  NS_ConvertUTF16toUTF8(mName).get(),
                  charmap->SizeOfIncludingThis(moz_malloc_size_of),
                  charmap->mHash, mCharacterMap == charmap ? " new" : ""));
    if (LOG_CMAPDATA_ENABLED()) {
        char prefix[256];
        SprintfLiteral(prefix, "(cmapdata) name: %.220s",
                       NS_ConvertUTF16toUTF8(mName).get());
        charmap->Dump(prefix, eGfxLog_cmapdata);
    }

    return rv;
}
Example #17
0
int CALLBACK GDIFontInfo::EnumerateFontsForFamily(
                 const ENUMLOGFONTEXW *lpelfe,
                 const NEWTEXTMETRICEXW *nmetrics,
                 DWORD fontType, LPARAM data)
{
    EnumerateFontsForFamilyData *famData =
        reinterpret_cast<EnumerateFontsForFamilyData*>(data);
    HDC hdc = famData->mFontInfo.mHdc;
    LOGFONTW logFont = lpelfe->elfLogFont;
    const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;

    AutoSelectFont font(hdc, &logFont);
    if (!font.IsValid()) {
        return 1;
    }

    FontFaceData fontData;
    nsDependentString fontName(lpelfe->elfFullName);

    // callback called for each style-charset so return if style already seen
    if (fontName.Equals(famData->mPreviousFontName)) {
        return 1;
    }
    famData->mPreviousFontName = fontName;
    famData->mFontInfo.mLoadStats.fonts++;

    // read name table info
    bool nameDataLoaded = false;
    if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) {
        uint32_t kNAME =
            NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e'));
        uint32_t nameSize;
        AutoTArray<uint8_t, 1024> nameData;

        nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0);
        if (nameSize != GDI_ERROR &&
            nameSize > 0 &&
            nameData.SetLength(nameSize, fallible)) {
            ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize);

            // face names
            if (famData->mFontInfo.mLoadFaceNames) {
                gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize,
                                                gfxFontUtils::NAME_ID_FULL,
                                                fontData.mFullName);
                gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize,
                                                gfxFontUtils::NAME_ID_POSTSCRIPT,
                                                fontData.mPostscriptName);
                nameDataLoaded = true;
                famData->mFontInfo.mLoadStats.facenames++;
            }

            // other family names
            if (famData->mFontInfo.mLoadOtherNames) {
                gfxFontFamily::ReadOtherFamilyNamesForFace(famData->mFamilyName,
                                                           (const char*)(nameData.Elements()),
                                                           nameSize,
                                                           famData->mOtherFamilyNames,
                                                           false);
            }
        }
    }

    // read cmap
    bool cmapLoaded = false;
    gfxWindowsFontType feType =
        GDIFontEntry::DetermineFontType(metrics, fontType);
    if (famData->mFontInfo.mLoadCmaps &&
        (feType == GFX_FONT_TYPE_PS_OPENTYPE ||
         feType == GFX_FONT_TYPE_TT_OPENTYPE ||
         feType == GFX_FONT_TYPE_TRUETYPE))
    {
        uint32_t kCMAP =
            NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p'));
        uint32_t cmapSize;
        AutoTArray<uint8_t, 1024> cmapData;

        cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0);
        if (cmapSize != GDI_ERROR &&
            cmapSize > 0 &&
            cmapData.SetLength(cmapSize, fallible)) {
            ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize);
            bool cmapLoaded = false;
            bool unicodeFont = false, symbolFont = false;
            RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
            uint32_t offset;

            if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(),
                                                    cmapSize, *charmap,
                                                    offset, unicodeFont,
                                                    symbolFont))) {
                fontData.mCharacterMap = charmap;
                fontData.mUVSOffset = offset;
                fontData.mSymbolFont = symbolFont;
                cmapLoaded = true;
                famData->mFontInfo.mLoadStats.cmaps++;
            }
        }
    }

    if (cmapLoaded || nameDataLoaded) {
        famData->mFontInfo.mFontFaceData.Put(fontName, fontData);
    }

    return famData->mFontInfo.mCanceled ? 0 : 1;
}
Example #18
0
nsresult
nsLineBreaker::AppendText(nsIAtom* aHyphenationLanguage, const uint8_t* aText, uint32_t aLength,
                          uint32_t aFlags, nsILineBreakSink* aSink)
{
  NS_ASSERTION(aLength > 0, "Appending empty text...");

  if (aFlags & (BREAK_NEED_CAPITALIZATION | BREAK_USE_AUTO_HYPHENATION)) {
    // Defer to the Unicode path if capitalization or hyphenation is required
    nsAutoString str;
    const char* cp = reinterpret_cast<const char*>(aText);
    CopyASCIItoUTF16(nsDependentCSubstring(cp, cp + aLength), str);
    return AppendText(aHyphenationLanguage, str.get(), aLength, aFlags, aSink);
  }

  uint32_t offset = 0;

  // Continue the current word
  if (mCurrentWord.Length() > 0) {
    NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set");

    while (offset < aLength && !IsSpace(aText[offset])) {
      mCurrentWord.AppendElement(aText[offset]);
      if (!mCurrentWordContainsComplexChar &&
          IsComplexASCIIChar(aText[offset])) {
        mCurrentWordContainsComplexChar = true;
      }
      ++offset;
    }

    if (offset > 0) {
      mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags));
    }

    if (offset == aLength) {
      // We did not encounter whitespace so the word hasn't finished yet.
      return NS_OK;
    }

    // We encountered whitespace, so we're done with this word
    nsresult rv = FlushCurrentWord();
    if (NS_FAILED(rv))
      return rv;
  }

  AutoTArray<uint8_t,4000> breakState;
  if (aSink) {
    if (!breakState.AppendElements(aLength))
      return NS_ERROR_OUT_OF_MEMORY;
  }

  uint32_t start = offset;
  bool noBreaksNeeded = !aSink ||
    ((aFlags & NO_BREAKS_NEEDED_FLAGS) == NO_BREAKS_NEEDED_FLAGS &&
     !mBreakHere && !mAfterBreakableSpace);
  if (noBreaksNeeded) {
    // Skip to the space before the last word, since either the break data
    // here is not needed, or no breaks are set in the sink and there cannot
    // be any breaks in this chunk; all we need is the context for the next
    // chunk (if any)
    offset = aLength;
    while (offset > start) {
      --offset;
      if (IsSpace(aText[offset]))
        break;
    }
  }
  uint32_t wordStart = offset;
  bool wordHasComplexChar = false;

  for (;;) {
    uint8_t ch = aText[offset];
    bool isSpace = IsSpace(ch);
    bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);

    if (aSink) {
      // Consider word-break style.  Since the break position of CJK scripts
      // will be set by nsILineBreaker, we don't consider CJK at this point.
      breakState[offset] =
        mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
        (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ?
          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
    }
    mBreakHere = false;
    mAfterBreakableSpace = isBreakableSpace;

    if (isSpace) {
      if (offset > wordStart && wordHasComplexChar) {
        if (aSink && !(aFlags & BREAK_SUPPRESS_INSIDE)) {
          // Save current start-of-word state because GetJISx4051Breaks will
          // set it to false
          uint8_t currentStart = breakState[wordStart];
          nsContentUtils::LineBreaker()->
            GetJISx4051Breaks(aText + wordStart, offset - wordStart,
                              mWordBreak,
                              breakState.Elements() + wordStart);
          breakState[wordStart] = currentStart;
        }
        wordHasComplexChar = false;
      }

      ++offset;
      if (offset >= aLength)
        break;
      wordStart = offset;
    } else {
      if (!wordHasComplexChar && IsComplexASCIIChar(ch)) {
        wordHasComplexChar = true;
      }
      ++offset;
      if (offset >= aLength) {
        // Save this word
        mCurrentWordContainsComplexChar = wordHasComplexChar;
        uint32_t len = offset - wordStart;
        char16_t* elems = mCurrentWord.AppendElements(len);
        if (!elems)
          return NS_ERROR_OUT_OF_MEMORY;
        uint32_t i;
        for (i = wordStart; i < offset; ++i) {
          elems[i - wordStart] = aText[i];
        }
        mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags));
        // Ensure that the break-before for this word is written out
        offset = wordStart + 1;
        break;
      }
    }
  }

  if (!noBreaksNeeded) {
    aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
  }
  return NS_OK;
}
gfxFontconfigUtils::LangSupportEntry *
gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, bool aWithFonts)
{
    // Currently any unrecognized languages from documents will be converted
    // to x-unicode by nsILanguageAtomService, so there is a limit on the
    // langugages that will be added here.  Reconsider when/if document
    // languages are passed to this routine.

    LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang);
    if (!entry)
        return nullptr;

    FcLangResult best = FcLangDifferentLang;

    if (!entry->IsKeyInitialized()) {
        entry->InitKey(aLang);
    } else {
        // mSupport is already initialized.
        if (!aWithFonts)
            return entry;

        best = entry->mSupport;
        // If there is support for this language, an empty font list indicates
        // that the list hasn't been initialized yet.
        if (best == FcLangDifferentLang || entry->mFonts.Length() > 0)
            return entry;
    }

    // These FcFontSets are owned by fontconfig
    FcFontSet *fontSets[] = {
        FcConfigGetFonts(nullptr, FcSetSystem)
#ifdef MOZ_BUNDLED_FONTS
        , FcConfigGetFonts(nullptr, FcSetApplication)
#endif
    };

    AutoTArray<FcPattern*,100> fonts;

    for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) {
        FcFontSet *fontSet = fontSets[fs];
        if (!fontSet) {
            continue;
        }
        for (int f = 0; f < fontSet->nfont; ++f) {
            FcPattern *font = fontSet->fonts[f];

            FcLangResult support = GetLangSupport(font, aLang);

            if (support < best) { // lower is better
                best = support;
                if (aWithFonts) {
                    fonts.Clear();
                } else if (best == FcLangEqual) {
                    break;
                }
            }

            // The font list in the LangSupportEntry is expected to be used
            // only when no default fonts support the language.  There would
            // be a large number of fonts in entries for languages using Latin
            // script but these do not need to be created because default
            // fonts already support these languages.
            if (aWithFonts && support != FcLangDifferentLang &&
                support == best) {
                fonts.AppendElement(font);
            }
        }
    }

    entry->mSupport = best;
    if (aWithFonts) {
        if (fonts.Length() != 0) {
            entry->mFonts.AppendElements(fonts.Elements(), fonts.Length());
        } else if (best != FcLangDifferentLang) {
            // Previously there was a font that supported this language at the
            // level of entry->mSupport, but it has now disappeared.  At least
            // entry->mSupport needs to be recalculated, but this is an
            // indication that the set of installed fonts has changed, so
            // update all caches.
            mLastConfig = nullptr; // invalidates caches
            UpdateFontListInternal(true);
            return GetLangSupportEntry(aLang, aWithFonts);
        }
    }

    return entry;
}
Example #20
0
void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold)
{

  pixman_box32_t *boxes;
  int n;
  boxes = pixman_region32_rectangles(&mImpl, &n);

  // if we have no rectangles then we're done
  if (!n)
    return;

  pixman_box32_t *end = boxes + n;
  pixman_box32_t *topRectsEnd = boxes+1;
  pixman_box32_t *topRects = boxes;

  // we need some temporary storage for merging both rows of rectangles
  AutoTArray<pixman_box32_t, 10> tmpStorage;
  tmpStorage.SetCapacity(n);
  pixman_box32_t *tmpRect = tmpStorage.Elements();

  pixman_box32_t *destRect = boxes;
  pixman_box32_t *rect = tmpRect;
  // find the end of the first span of rectangles
  while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) {
    topRectsEnd++;
  }

  // if we only have one row we are done
  if (topRectsEnd == end)
    return;

  pixman_box32_t *bottomRects = topRectsEnd;
  pixman_box32_t *bottomRectsEnd = bottomRects+1;
  do {
    // find the end of the bottom span of rectangles
    while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) {
      bottomRectsEnd++;
    }
    uint32_t totalArea = ComputeMergedAreaIncrease(topRects, topRectsEnd,
                                                   bottomRects, bottomRectsEnd);

    if (totalArea <= aThreshold) {
      // merge the rects into tmpRect
      rect = MergeRects(topRects, topRectsEnd, bottomRects, bottomRectsEnd, tmpRect);

      // set topRects to where the newly merged rects will be so that we use them
      // as our next set of topRects
      topRects = destRect;
      // copy the merged rects back into the destination
      topRectsEnd = CopyRow(destRect, tmpRect, rect);
    } else {
      // copy the unmerged rects
      destRect = CopyRow(destRect, topRects, topRectsEnd);

      topRects = bottomRects;
      topRectsEnd = bottomRectsEnd;
      if (bottomRectsEnd == end) {
        // copy the last row when we are done
        topRectsEnd = CopyRow(destRect, topRects, topRectsEnd);
      }
    }
    bottomRects = bottomRectsEnd;
  } while (bottomRectsEnd != end);


  uint32_t reducedCount = topRectsEnd - pixman_region32_rectangles(&this->mImpl, &n);
  // pixman has a special representation for
  // regions of 1 rectangle. So just use the
  // bounds in that case
  if (reducedCount > 1) {
    // reach into pixman and lower the number
    // of rects stored in data.
    this->mImpl.data->numRects = reducedCount;
  } else {
    *this = GetBounds();
  }
}
void
MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
                                     mozilla::gfx::DrawTarget* aRefDrawTarget,
                                     gfxMissingFontRecorder* aMFR)
{
  gfxFontGroup* fontGroup = aTextRun->GetFontGroup();

  nsAutoString convertedString;
  AutoTArray<bool,50> charsToMergeArray;
  AutoTArray<bool,50> deletedCharsArray;
  AutoTArray<RefPtr<nsTransformedCharStyle>,50> styleArray;
  AutoTArray<uint8_t,50> canBreakBeforeArray;
  bool mergeNeeded = false;

  bool singleCharMI =
    !!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SINGLE_CHAR_MI);

  uint32_t length = aTextRun->GetLength();
  const char16_t* str = aTextRun->mString.BeginReading();
  const nsTArray<RefPtr<nsTransformedCharStyle>>& styles = aTextRun->mStyles;
  nsFont font;
  if (length) {
    font = styles[0]->mFont;

    if (mSSTYScriptLevel || (mFlags & MATH_FONT_FEATURE_DTLS)) {
      bool foundSSTY = false;
      bool foundDTLS = false;
      // We respect ssty settings explicitly set by the user
      for (uint32_t i = 0; i < font.fontFeatureSettings.Length(); i++) {
        if (font.fontFeatureSettings[i].mTag == TT_SSTY) {
          foundSSTY = true;
        } else if (font.fontFeatureSettings[i].mTag == TT_DTLS) {
          foundDTLS = true;
        }
      }
      if (mSSTYScriptLevel && !foundSSTY) {
        uint8_t sstyLevel = 0;
        float scriptScaling = pow(styles[0]->mScriptSizeMultiplier,
                                  mSSTYScriptLevel);
        static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER < 1,
                      "Shouldn't it make things smaller?");
        /*
          An SSTY level of 2 is set if the scaling factor is less than or equal
          to halfway between that for a scriptlevel of 1 (0.71) and that of a
          scriptlevel of 2 (0.71^2), assuming the default script size multiplier.
          An SSTY level of 1 is set if the script scaling factor is less than
          or equal that for a scriptlevel of 1 assuming the default script size
          multiplier.

          User specified values of script size multiplier will change the scaling
          factor which mSSTYScriptLevel values correspond to.

          In the event that the script size multiplier actually makes things
          larger, no change is made.

          To opt out of this change, add the following to the stylesheet:
          "font-feature-settings: 'ssty' 0"
        */
        if (scriptScaling <= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER +
                              (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER *
                               NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER))/2) {
          // Currently only the first two ssty settings are used, so two is large
          // as we go
          sstyLevel = 2;
        } else if (scriptScaling <= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) {
          sstyLevel = 1;
        }
        if (sstyLevel) {
          gfxFontFeature settingSSTY;
          settingSSTY.mTag = TT_SSTY;
          settingSSTY.mValue = sstyLevel;
          font.fontFeatureSettings.AppendElement(settingSSTY);
        }
      }
      /*
        Apply the dtls font feature setting (dotless).
        This gets applied to the base frame and all descendants of the base
        frame of certain <mover> and <munderover> frames.

        See nsMathMLmunderoverFrame.cpp for a full description.

        To opt out of this change, add the following to the stylesheet:
        "font-feature-settings: 'dtls' 0"
      */
      if ((mFlags & MATH_FONT_FEATURE_DTLS) && !foundDTLS) {
        gfxFontFeature settingDTLS;
        settingDTLS.mTag = TT_DTLS;
        settingDTLS.mValue = 1;
        font.fontFeatureSettings.AppendElement(settingDTLS);
      }
    }
  }

  uint8_t mathVar = NS_MATHML_MATHVARIANT_NONE;
  bool doMathvariantStyling = true;

  for (uint32_t i = 0; i < length; ++i) {
    int extraChars = 0;
    mathVar = styles[i]->mMathVariant;

    if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) {
      // If the user has explicitly set a non-default value for fontstyle or
      // fontweight, the italic mathvariant behaviour of <mi> is disabled
      // This overrides the initial values specified in fontStyle, to avoid
      // inconsistencies in which attributes allow CSS changes and which do not.
      if (mFlags & MATH_FONT_WEIGHT_BOLD) {
        font.weight = NS_FONT_WEIGHT_BOLD;
        if (mFlags & MATH_FONT_STYLING_NORMAL) {
          font.style = NS_FONT_STYLE_NORMAL;
        } else {
          font.style = NS_FONT_STYLE_ITALIC;
        }
      } else if (mFlags & MATH_FONT_STYLING_NORMAL) {
        font.style = NS_FONT_STYLE_NORMAL;
        font.weight = NS_FONT_WEIGHT_NORMAL;
      } else {
        mathVar = NS_MATHML_MATHVARIANT_ITALIC;
      }
    }

    uint32_t ch = str[i];
    if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 &&
        NS_IS_LOW_SURROGATE(str[i + 1])) {
      ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
    }
    uint32_t ch2 = MathVariant(ch, mathVar);

    if (mathVar == NS_MATHML_MATHVARIANT_BOLD ||
        mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC ||
        mathVar == NS_MATHML_MATHVARIANT_ITALIC) {
      if (ch == ch2  && ch != 0x20 && ch != 0xA0) {
        // Don't apply the CSS style if a character cannot be
        // transformed. There is an exception for whitespace as it is both
        // common and innocuous.
        doMathvariantStyling = false;
      }
      if (ch2 != ch) {
        // Bug 930504. Some platforms do not have fonts for Mathematical
        // Alphanumeric Symbols. Hence we check whether the transformed
        // character is actually available.
        uint8_t matchType;
        RefPtr<gfxFont> mathFont = fontGroup->
          FindFontForChar(ch2, 0, 0, unicode::Script::COMMON, nullptr, &matchType);
        if (mathFont) {
          // Don't apply the CSS style if there is a math font for at least one
          // of the transformed character in this text run.
          doMathvariantStyling = false;
        } else {
          // We fallback to the original character.
          ch2 = ch;
          if (aMFR) {
            aMFR->RecordScript(unicode::Script::MATHEMATICAL_NOTATION);
          }
        }
      }
    }

    deletedCharsArray.AppendElement(false);
    charsToMergeArray.AppendElement(false);
    styleArray.AppendElement(styles[i]);
    canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));

    if (IS_IN_BMP(ch2)) {
      convertedString.Append(ch2);
    } else {
      convertedString.Append(H_SURROGATE(ch2));
      convertedString.Append(L_SURROGATE(ch2));
      ++extraChars;
      if (!IS_IN_BMP(ch)) {
        deletedCharsArray.AppendElement(true); // not exactly deleted, but
                                          // the trailing surrogate is skipped
        ++i;
      }
    }

    while (extraChars-- > 0) {
      mergeNeeded = true;
      charsToMergeArray.AppendElement(true);
      styleArray.AppendElement(styles[i]);
      canBreakBeforeArray.AppendElement(false);
    }
  }

  gfx::ShapedTextFlags flags;
  gfxTextRunFactory::Parameters innerParams =
      GetParametersForInner(aTextRun, &flags, aRefDrawTarget);

  RefPtr<nsTransformedTextRun> transformedChild;
  RefPtr<gfxTextRun> cachedChild;
  gfxTextRun* child;

  if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) {
    font.style = NS_FONT_STYLE_NORMAL;
    font.weight = NS_FONT_WEIGHT_BOLD;
  } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) {
    font.style = NS_FONT_STYLE_ITALIC;
    font.weight = NS_FONT_WEIGHT_NORMAL;
  } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC &&
             doMathvariantStyling) {
    font.style = NS_FONT_STYLE_ITALIC;
    font.weight = NS_FONT_WEIGHT_BOLD;
  } else if (mathVar != NS_MATHML_MATHVARIANT_NONE) {
    // Mathvariant overrides fontstyle and fontweight
    // Need to check to see if mathvariant is actually applied as this function
    // is used for other purposes.
    font.style = NS_FONT_STYLE_NORMAL;
    font.weight = NS_FONT_WEIGHT_NORMAL;
  }
  gfxFontGroup* newFontGroup = nullptr;

  // Get the correct gfxFontGroup that corresponds to the earlier font changes.
  if (length) {
    font.size = NSToCoordRound(font.size * mFontInflation);
    nsPresContext* pc = styles[0]->mPresContext;
    nsFontMetrics::Params params;
    params.language = styles[0]->mLanguage;
    params.explicitLanguage = styles[0]->mExplicitLanguage;
    params.userFontSet = pc->GetUserFontSet();
    params.textPerf = pc->GetTextPerfMetrics();
    RefPtr<nsFontMetrics> metrics =
      pc->DeviceContext()->GetMetricsFor(font, params);
    newFontGroup = metrics->GetThebesFontGroup();
  }

  if (!newFontGroup) {
    // If we can't get a new font group, fall back to the old one.  Rendering
    // will be incorrect, but not significantly so.
    newFontGroup = fontGroup;
  }

  if (mInnerTransformingTextRunFactory) {
    transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
        convertedString.BeginReading(), convertedString.Length(),
        &innerParams, newFontGroup, flags, nsTextFrameUtils::Flags(),
        Move(styleArray), false);
    child = transformedChild.get();
  } else {
    cachedChild = newFontGroup->MakeTextRun(
        convertedString.BeginReading(), convertedString.Length(),
        &innerParams, flags, nsTextFrameUtils::Flags(), aMFR);
    child = cachedChild.get();
  }
  if (!child)
    return;

  typedef gfxTextRun::Range Range;

  // Copy potential linebreaks into child so they're preserved
  // (and also child will be shaped appropriately)
  NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
               "Dropped characters or break-before values somewhere!");
  Range range(0, uint32_t(canBreakBeforeArray.Length()));
  child->SetPotentialLineBreaks(range, canBreakBeforeArray.Elements());
  if (transformedChild) {
    transformedChild->FinishSettingProperties(aRefDrawTarget, aMFR);
  }

  if (mergeNeeded) {
    // Now merge multiple characters into one multi-glyph character as required
    NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(),
                 "source length mismatch");
    NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(),
                 "destination length mismatch");
    MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(),
                             deletedCharsArray.Elements());
  } else {
    // No merging to do, so just copy; this produces a more optimized textrun.
    // We can't steal the data because the child may be cached and stealing
    // the data would break the cache.
    aTextRun->ResetGlyphRuns();
    aTextRun->CopyGlyphDataFrom(child, Range(child), 0);
  }
}
Example #22
0
nsresult
nsLineBreaker::FlushCurrentWord()
{
  uint32_t length = mCurrentWord.Length();
  AutoTArray<uint8_t,4000> breakState;
  if (!breakState.AppendElements(length))
    return NS_ERROR_OUT_OF_MEMORY;
  
  nsTArray<bool> capitalizationState;

  if (!mCurrentWordContainsComplexChar) {
    // For break-strict set everything internal to "break", otherwise
    // to "no break"!
    memset(breakState.Elements(),
           mWordBreak == nsILineBreaker::kWordBreak_BreakAll ?
             gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
             gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
           length*sizeof(uint8_t));
  } else {
    nsContentUtils::LineBreaker()->
      GetJISx4051Breaks(mCurrentWord.Elements(), length, mWordBreak,
                        breakState.Elements());
  }

  bool autoHyphenate = mCurrentWordLanguage &&
    !mCurrentWordContainsMixedLang;
  uint32_t i;
  for (i = 0; autoHyphenate && i < mTextItems.Length(); ++i) {
    TextItem* ti = &mTextItems[i];
    if (!(ti->mFlags & BREAK_USE_AUTO_HYPHENATION)) {
      autoHyphenate = false;
    }
  }
  if (autoHyphenate) {
    RefPtr<nsHyphenator> hyphenator =
      nsHyphenationManager::Instance()->GetHyphenator(mCurrentWordLanguage);
    if (hyphenator) {
      FindHyphenationPoints(hyphenator,
                            mCurrentWord.Elements(),
                            mCurrentWord.Elements() + length,
                            breakState.Elements());
    }
  }

  uint32_t offset = 0;
  for (i = 0; i < mTextItems.Length(); ++i) {
    TextItem* ti = &mTextItems[i];
    NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?");

    if ((ti->mFlags & BREAK_SUPPRESS_INITIAL) && ti->mSinkOffset == 0) {
      breakState[offset] = gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
    }
    if (ti->mFlags & BREAK_SUPPRESS_INSIDE) {
      uint32_t exclude = ti->mSinkOffset == 0 ? 1 : 0;
      memset(breakState.Elements() + offset + exclude,
             gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
             (ti->mLength - exclude)*sizeof(uint8_t));
    }

    // Don't set the break state for the first character of the word, because
    // it was already set correctly earlier and we don't know what the true
    // value should be.
    uint32_t skipSet = i == 0 ? 1 : 0;
    if (ti->mSink) {
      ti->mSink->SetBreaks(ti->mSinkOffset + skipSet, ti->mLength - skipSet,
                           breakState.Elements() + offset + skipSet);

      if (ti->mFlags & BREAK_NEED_CAPITALIZATION) {
        if (capitalizationState.Length() == 0) {
          if (!capitalizationState.AppendElements(length))
            return NS_ERROR_OUT_OF_MEMORY;
          memset(capitalizationState.Elements(), false, length*sizeof(bool));
          SetupCapitalization(mCurrentWord.Elements(), length,
                              capitalizationState.Elements());
        }
        ti->mSink->SetCapitalization(ti->mSinkOffset, ti->mLength,
                                     capitalizationState.Elements() + offset);
      }
    }
    
    offset += ti->mLength;
  }

  mCurrentWord.Clear();
  mTextItems.Clear();
  mCurrentWordContainsComplexChar = false;
  mCurrentWordContainsMixedLang = false;
  mCurrentWordLanguage = nullptr;
  return NS_OK;
}
nsresult
gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
                                    uint32_t       aOffset,
                                    uint32_t       aLength,
                                    CTRunRef       aCTRun,
                                    int32_t        aStringOffset)
{
    // The word has been bidi-wrapped; aStringOffset is the number
    // of chars at the beginning of the CTLine that we should skip.
    // aCTRun is a glyph run from the CoreText layout process.

    int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;

    int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
    if (numGlyphs == 0) {
        return NS_OK;
    }

    int32_t wordLength = aLength;

    // character offsets get really confusing here, as we have to keep track of
    // (a) the text in the actual textRun we're constructing
    // (c) the string that was handed to CoreText, which contains the text of the font run
    //     plus directional-override padding
    // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line
    //     (but may extend beyond the actual font run into the bidi wrapping text).
    //     aStringOffset tells us how many initial characters of the line to ignore.

    // get the source string range within the CTLine's text
    CFRange stringRange = ::CTRunGetStringRange(aCTRun);
    // skip the run if it is entirely outside the actual range of the font run
    if (stringRange.location - aStringOffset + stringRange.length <= 0 ||
        stringRange.location - aStringOffset >= wordLength) {
        return NS_OK;
    }

    // retrieve the laid-out glyph data from the CTRun
    UniquePtr<CGGlyph[]> glyphsArray;
    UniquePtr<CGPoint[]> positionsArray;
    UniquePtr<CFIndex[]> glyphToCharArray;
    const CGGlyph* glyphs = nullptr;
    const CGPoint* positions = nullptr;
    const CFIndex* glyphToChar = nullptr;

    // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
    // and so allocating a new array and copying data with CTRunGetGlyphs
    // will be extremely rare.
    // If this were not the case, we could use an AutoTArray<> to
    // try and avoid the heap allocation for small runs.
    // It's possible that some future change to CoreText will mean that
    // CTRunGetGlyphsPtr fails more often; if this happens, AutoTArray<>
    // may become an attractive option.
    glyphs = ::CTRunGetGlyphsPtr(aCTRun);
    if (!glyphs) {
        glyphsArray = MakeUniqueFallible<CGGlyph[]>(numGlyphs);
        if (!glyphsArray) {
            return NS_ERROR_OUT_OF_MEMORY;
        }
        ::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get());
        glyphs = glyphsArray.get();
    }

    positions = ::CTRunGetPositionsPtr(aCTRun);
    if (!positions) {
        positionsArray = MakeUniqueFallible<CGPoint[]>(numGlyphs);
        if (!positionsArray) {
            return NS_ERROR_OUT_OF_MEMORY;
        }
        ::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get());
        positions = positionsArray.get();
    }

    // Remember that the glyphToChar indices relate to the CoreText line,
    // not to the beginning of the textRun, the font run,
    // or the stringRange of the glyph run
    glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun);
    if (!glyphToChar) {
        glyphToCharArray = MakeUniqueFallible<CFIndex[]>(numGlyphs);
        if (!glyphToCharArray) {
            return NS_ERROR_OUT_OF_MEMORY;
        }
        ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
        glyphToChar = glyphToCharArray.get();
    }

    double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
                                                  nullptr, nullptr, nullptr);

    AutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
    gfxShapedText::CompressedGlyph *charGlyphs =
        aShapedText->GetCharacterGlyphs() + aOffset;

    // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
    // to a source text character; we also need the charindex-to-glyphindex mapping to
    // find the glyph for a given char. Note that some chars may not map to any glyph
    // (ligature continuations), and some may map to several glyphs (eg Indic split vowels).
    // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we
    // record the last glyph index for cases where the char maps to several glyphs,
    // so that our clumping will include all the glyph fragments for the character.

    // The charToGlyph array is indexed by char position within the stringRange of the glyph run.

    static const int32_t NO_GLYPH = -1;
    AutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
    if (!charToGlyphArray.SetLength(stringRange.length, fallible)) {
        return NS_ERROR_OUT_OF_MEMORY;
    }
    int32_t *charToGlyph = charToGlyphArray.Elements();
    for (int32_t offset = 0; offset < stringRange.length; ++offset) {
        charToGlyph[offset] = NO_GLYPH;
    }
    for (int32_t i = 0; i < numGlyphs; ++i) {
        int32_t loc = glyphToChar[i] - stringRange.location;
        if (loc >= 0 && loc < stringRange.length) {
            charToGlyph[loc] = i;
        }
    }

    // Find character and glyph clumps that correspond, allowing for ligatures,
    // indic reordering, split glyphs, etc.
    //
    // The idea is that we'll find a character sequence starting at the first char of stringRange,
    // and extend it until it includes the character associated with the first glyph;
    // we also extend it as long as there are "holes" in the range of glyphs. So we
    // will eventually have a contiguous sequence of characters, starting at the beginning
    // of the range, that map to a contiguous sequence of glyphs, starting at the beginning
    // of the glyph array. That's a clump; then we update the starting positions and repeat.
    //
    // NB: In the case of RTL layouts, we iterate over the stringRange in reverse.
    //

    // This may find characters that fall outside the range 0:wordLength,
    // so we won't necessarily use everything we find here.

    bool isRightToLeft = aShapedText->IsRightToLeft();
    int32_t glyphStart = 0; // looking for a clump that starts at this glyph index
    int32_t charStart = isRightToLeft ?
        stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run)

    while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for
        bool inOrder = true;
        int32_t charEnd = glyphToChar[glyphStart] - stringRange.location;
        NS_WARN_IF_FALSE(charEnd >= 0 && charEnd < stringRange.length,
                         "glyph-to-char mapping points outside string range");
        // clamp charEnd to the valid range of the string
        charEnd = std::max(charEnd, 0);
        charEnd = std::min(charEnd, int32_t(stringRange.length));

        int32_t glyphEnd = glyphStart;
        int32_t charLimit = isRightToLeft ? -1 : stringRange.length;
        do {
            // This is normally executed once for each iteration of the outer loop,
            // but in unusual cases where the character/glyph association is complex,
            // the initial character range might correspond to a non-contiguous
            // glyph range with "holes" in it. If so, we will repeat this loop to
            // extend the character range until we have a contiguous glyph sequence.
            NS_ASSERTION((direction > 0 && charEnd < charLimit) ||
                         (direction < 0 && charEnd > charLimit),
                         "no characters left in range?");
            charEnd += direction;
            while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
                charEnd += direction;
            }

            // find the maximum glyph index covered by the clump so far
            if (isRightToLeft) {
                for (int32_t i = charStart; i > charEnd; --i) {
                    if (charToGlyph[i] != NO_GLYPH) {
                        // update extent of glyph range
                        glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
                    }
                }
            } else {
                for (int32_t i = charStart; i < charEnd; ++i) {
                    if (charToGlyph[i] != NO_GLYPH) {
                        // update extent of glyph range
                        glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
                    }
                }
            }

            if (glyphEnd == glyphStart + 1) {
                // for the common case of a single-glyph clump, we can skip the following checks
                break;
            }

            if (glyphEnd == glyphStart) {
                // no glyphs, try to extend the clump
                continue;
            }

            // check whether all glyphs in the range are associated with the characters
            // in our clump; if not, we have a discontinuous range, and should extend it
            // unless we've reached the end of the text
            bool allGlyphsAreWithinCluster = true;
            int32_t prevGlyphCharIndex = charStart;
            for (int32_t i = glyphStart; i < glyphEnd; ++i) {
                int32_t glyphCharIndex = glyphToChar[i] - stringRange.location;
                if (isRightToLeft) {
                    if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) {
                        allGlyphsAreWithinCluster = false;
                        break;
                    }
                    if (glyphCharIndex > prevGlyphCharIndex) {
                        inOrder = false;
                    }
                    prevGlyphCharIndex = glyphCharIndex;
                } else {
                    if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
                        allGlyphsAreWithinCluster = false;
                        break;
                    }
                    if (glyphCharIndex < prevGlyphCharIndex) {
                        inOrder = false;
                    }
                    prevGlyphCharIndex = glyphCharIndex;
                }
            }
            if (allGlyphsAreWithinCluster) {
                break;
            }
        } while (charEnd != charLimit);

        NS_WARN_IF_FALSE(glyphStart < glyphEnd,
                         "character/glyph clump contains no glyphs!");
        if (glyphStart == glyphEnd) {
            ++glyphStart; // make progress - avoid potential infinite loop
            charStart = charEnd;
            continue;
        }

        NS_WARN_IF_FALSE(charStart != charEnd,
                         "character/glyph clump contains no characters!");
        if (charStart == charEnd) {
            glyphStart = glyphEnd; // this is bad - we'll discard the glyph(s),
                                   // as there's nowhere to attach them
            continue;
        }

        // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
        // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
        // and endCharIndex to the limit (position beyond the last char),
        // adjusting for the offset of the stringRange relative to the textRun.
        int32_t baseCharIndex, endCharIndex;
        if (isRightToLeft) {
            while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) {
                charEnd--;
            }
            baseCharIndex = charEnd + stringRange.location - aStringOffset + 1;
            endCharIndex = charStart + stringRange.location - aStringOffset + 1;
        } else {
            while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) {
                charEnd++;
            }
            baseCharIndex = charStart + stringRange.location - aStringOffset;
            endCharIndex = charEnd + stringRange.location - aStringOffset;
        }

        // Then we check if the clump falls outside our actual string range; if so, just go to the next.
        if (endCharIndex <= 0 || baseCharIndex >= wordLength) {
            glyphStart = glyphEnd;
            charStart = charEnd;
            continue;
        }
        // Ensure we won't try to go beyond the valid length of the word's text
        baseCharIndex = std::max(baseCharIndex, 0);
        endCharIndex = std::min(endCharIndex, wordLength);

        // Now we're ready to set the glyph info in the textRun; measure the glyph width
        // of the first (perhaps only) glyph, to see if it is "Simple"
        int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
        double toNextGlyph;
        if (glyphStart < numGlyphs-1) {
            toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
        } else {
            toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
        }
        int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit);

        // Check if it's a simple one-to-one mapping
        int32_t glyphsInClump = glyphEnd - glyphStart;
        if (glyphsInClump == 1 &&
            gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) &&
            gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
            charGlyphs[baseCharIndex].IsClusterStart() &&
            positions[glyphStart].y == 0.0)
        {
            charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
                                                     glyphs[glyphStart]);
        } else {
            // collect all glyphs in a list to be assigned to the first char;
            // there must be at least one in the clump, and we already measured its advance,
            // hence the placement of the loop-exit test and the measurement of the next glyph
            while (1) {
                gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
                details->mGlyphID = glyphs[glyphStart];
                details->mXOffset = 0;
                details->mYOffset = -positions[glyphStart].y * appUnitsPerDevUnit;
                details->mAdvance = advance;
                if (++glyphStart >= glyphEnd) {
                   break;
                }
                if (glyphStart < numGlyphs-1) {
                    toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
                } else {
                    toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
                }
                advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
            }

            gfxTextRun::CompressedGlyph textRunGlyph;
            textRunGlyph.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
                                    true, detailedGlyphs.Length());
            aShapedText->SetGlyphs(aOffset + baseCharIndex, textRunGlyph,
                                   detailedGlyphs.Elements());

            detailedGlyphs.Clear();
        }

        // the rest of the chars in the group are ligature continuations, no associated glyphs
        while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
            gfxShapedText::CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex];
            NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph");
            shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0);
        }

        glyphStart = glyphEnd;
        charStart = charEnd;
    }

    return NS_OK;
}
Example #24
0
nsresult
VorbisTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
{
  if (mEosSetInEncoder) {
    return NS_OK;
  }

  PROFILER_LABEL("VorbisTrackEncoder", "GetEncodedTrack",
    js::ProfileEntry::Category::OTHER);

  nsAutoPtr<AudioSegment> sourceSegment;
  sourceSegment = new AudioSegment();
  {
    // Move all the samples from mRawSegment to sourceSegment. We only hold
    // the monitor in this block.
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);

    // Wait if mEncoder is not initialized, or when not enough raw data, but is
    // not the end of stream nor is being canceled.
    while (!mCanceled && mRawSegment.GetDuration() < GetPacketDuration() &&
           !mEndOfStream) {
      mon.Wait();
    }
    VORBISLOG("GetEncodedTrack passes wait, duration is %lld\n",
      mRawSegment.GetDuration());
    if (mCanceled || mEncodingComplete) {
      return NS_ERROR_FAILURE;
    }

    sourceSegment->AppendFrom(&mRawSegment);
  }

  if (mEndOfStream && (sourceSegment->GetDuration() == 0)
      && !mEosSetInEncoder) {
    mEncodingComplete = true;
    mEosSetInEncoder = true;
    VORBISLOG("[Vorbis] Done encoding.");
    vorbis_analysis_wrote(&mVorbisDsp, 0);
    GetEncodedFrames(aData);

    return NS_OK;
  }

  // Start encoding data.
  AudioSegment::ChunkIterator iter(*sourceSegment);

  AudioDataValue **vorbisBuffer =
    vorbis_analysis_buffer(&mVorbisDsp, (int)sourceSegment->GetDuration());

  int framesCopied = 0;
  AutoTArray<AudioDataValue, 9600> interleavedPcm;
  AutoTArray<AudioDataValue, 9600> nonInterleavedPcm;
  interleavedPcm.SetLength(sourceSegment->GetDuration() * mChannels);
  nonInterleavedPcm.SetLength(sourceSegment->GetDuration() * mChannels);
  while (!iter.IsEnded()) {
    AudioChunk chunk = *iter;
    int frameToCopy = chunk.GetDuration();
    if (!chunk.IsNull()) {
      InterleaveTrackData(chunk, frameToCopy, mChannels,
                          interleavedPcm.Elements() + framesCopied * mChannels);
    } else { // empty data
      memset(interleavedPcm.Elements() + framesCopied * mChannels, 0,
             frameToCopy * mChannels * sizeof(AudioDataValue));
    }
    framesCopied += frameToCopy;
    iter.Next();
  }
  // De-interleave the interleavedPcm.
  DeInterleaveTrackData(interleavedPcm.Elements(), framesCopied, mChannels,
                        nonInterleavedPcm.Elements());
  // Copy the nonInterleavedPcm to vorbis buffer.
  for(uint8_t i = 0; i < mChannels; ++i) {
    memcpy(vorbisBuffer[i], nonInterleavedPcm.Elements() + framesCopied * i,
           framesCopied * sizeof(AudioDataValue));
  }

  // Now the vorbisBuffer contain the all data in non-interleaved.
  // Tell the library how much we actually submitted.
  vorbis_analysis_wrote(&mVorbisDsp, framesCopied);
  VORBISLOG("vorbis_analysis_wrote framesCopied %d\n", framesCopied);
  GetEncodedFrames(aData);

  return NS_OK;
}
Example #25
0
nsresult
nsLineBreaker::AppendText(nsIAtom* aHyphenationLanguage, const char16_t* aText, uint32_t aLength,
                          uint32_t aFlags, nsILineBreakSink* aSink)
{
  NS_ASSERTION(aLength > 0, "Appending empty text...");

  uint32_t offset = 0;

  // Continue the current word
  if (mCurrentWord.Length() > 0) {
    NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set");

    while (offset < aLength && !IsSpace(aText[offset])) {
      mCurrentWord.AppendElement(aText[offset]);
      if (!mCurrentWordContainsComplexChar && IsComplexChar(aText[offset])) {
        mCurrentWordContainsComplexChar = true;
      }
      UpdateCurrentWordLanguage(aHyphenationLanguage);
      ++offset;
    }

    if (offset > 0) {
      mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags));
    }

    if (offset == aLength)
      return NS_OK;

    // We encountered whitespace, so we're done with this word
    nsresult rv = FlushCurrentWord();
    if (NS_FAILED(rv))
      return rv;
  }

  AutoTArray<uint8_t,4000> breakState;
  if (aSink) {
    if (!breakState.AppendElements(aLength))
      return NS_ERROR_OUT_OF_MEMORY;
  }

  bool noCapitalizationNeeded = true;
  nsTArray<bool> capitalizationState;
  if (aSink && (aFlags & BREAK_NEED_CAPITALIZATION)) {
    if (!capitalizationState.AppendElements(aLength))
      return NS_ERROR_OUT_OF_MEMORY;
    memset(capitalizationState.Elements(), false, aLength*sizeof(bool));
    noCapitalizationNeeded = false;
  }

  uint32_t start = offset;
  bool noBreaksNeeded = !aSink ||
    ((aFlags & NO_BREAKS_NEEDED_FLAGS) == NO_BREAKS_NEEDED_FLAGS &&
     !mBreakHere && !mAfterBreakableSpace);
  if (noBreaksNeeded && noCapitalizationNeeded) {
    // Skip to the space before the last word, since either the break data
    // here is not needed, or no breaks are set in the sink and there cannot
    // be any breaks in this chunk; and we don't need to do word-initial
    // capitalization. All we need is the context for the next chunk (if any).
    offset = aLength;
    while (offset > start) {
      --offset;
      if (IsSpace(aText[offset]))
        break;
    }
  }
  uint32_t wordStart = offset;
  bool wordHasComplexChar = false;

  RefPtr<nsHyphenator> hyphenator;
  if ((aFlags & BREAK_USE_AUTO_HYPHENATION) &&
      !(aFlags & BREAK_SUPPRESS_INSIDE) &&
      aHyphenationLanguage) {
    hyphenator = nsHyphenationManager::Instance()->GetHyphenator(aHyphenationLanguage);
  }

  for (;;) {
    char16_t ch = aText[offset];
    bool isSpace = IsSpace(ch);
    bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);

    if (aSink && !noBreaksNeeded) {
      breakState[offset] =
        mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
        (mWordBreak == nsILineBreaker::kWordBreak_BreakAll)  ?
          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
    }
    mBreakHere = false;
    mAfterBreakableSpace = isBreakableSpace;

    if (isSpace) {
      if (offset > wordStart && aSink) {
        if (!(aFlags & BREAK_SUPPRESS_INSIDE)) {
          if (wordHasComplexChar) {
            // Save current start-of-word state because GetJISx4051Breaks will
            // set it to false
            uint8_t currentStart = breakState[wordStart];
            nsContentUtils::LineBreaker()->
              GetJISx4051Breaks(aText + wordStart, offset - wordStart,
                                mWordBreak,
                                breakState.Elements() + wordStart);
            breakState[wordStart] = currentStart;
          }
          if (hyphenator) {
            FindHyphenationPoints(hyphenator,
                                  aText + wordStart, aText + offset,
                                  breakState.Elements() + wordStart);
          }
        }
        if (!noCapitalizationNeeded) {
          SetupCapitalization(aText + wordStart, offset - wordStart,
                              capitalizationState.Elements() + wordStart);
        }
      }
      wordHasComplexChar = false;
      ++offset;
      if (offset >= aLength)
        break;
      wordStart = offset;
    } else {
      if (!wordHasComplexChar && IsComplexChar(ch)) {
        wordHasComplexChar = true;
      }
      ++offset;
      if (offset >= aLength) {
        // Save this word
        mCurrentWordContainsComplexChar = wordHasComplexChar;
        uint32_t len = offset - wordStart;
        char16_t* elems = mCurrentWord.AppendElements(len);
        if (!elems)
          return NS_ERROR_OUT_OF_MEMORY;
        memcpy(elems, aText + wordStart, sizeof(char16_t)*len);
        mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags));
        // Ensure that the break-before for this word is written out
        offset = wordStart + 1;
        UpdateCurrentWordLanguage(aHyphenationLanguage);
        break;
      }
    }
  }

  if (aSink) {
    if (!noBreaksNeeded) {
      aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
    }
    if (!noCapitalizationNeeded) {
      aSink->SetCapitalization(start, offset - start,
                               capitalizationState.Elements() + start);
    }
  }
  return NS_OK;
}