Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/image/encoders/png/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 23 kB image not shown  

Quelle  nsPNGEncoder.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "ImageLogging.h"
#include "nsCRT.h"
#include "nsPNGEncoder.h"
#include "nsStreamUtils.h"
#include "nsString.h"
#include "prprf.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/UniquePtrExtensions.h"

using namespace mozilla;

static LazyLogModule sPNGEncoderLog("PNGEncoder");

NS_IMPL_ISUPPORTS(nsPNGEncoder, imgIEncoder, nsIInputStream,
                  nsIAsyncInputStream)

#define DEFAULT_ZLIB_LEVEL 3
#define DEFAULT_FILTERS PNG_FILTER_SUB

nsPNGEncoder::nsPNGEncoder()
    : mPNG(nullptr),
      mPNGinfo(nullptr),
      mIsAnimation(false),
      mFinished(false),
      mImageBuffer(nullptr),
      mImageBufferSize(0),
      mImageBufferUsed(0),
      mImageBufferReadPoint(0),
      mCallback(nullptr),
      mCallbackTarget(nullptr),
      mNotifyThreshold(0),
      mReentrantMonitor("nsPNGEncoder.mReentrantMonitor") {}

nsPNGEncoder::~nsPNGEncoder() {
  if (mImageBuffer) {
    free(mImageBuffer);
    mImageBuffer = nullptr;
  }
  // don't leak if EndImageEncode wasn't called
  if (mPNG) {
    png_destroy_write_struct(&mPNG, &mPNGinfo);
  }
}

// nsPNGEncoder::InitFromData
//
//    One output option is supported: "transparency=none" means that the
//    output PNG will not have an alpha channel, even if the input does.
//
//    Based partially on gfx/cairo/cairo/src/cairo-png.c
//    See also media/libpng/libpng-manual.txt

NS_IMETHODIMP
nsPNGEncoder::InitFromData(const uint8_t* aData,
                           uint32_t aLength,  // (unused, req'd by JS)
                           uint32_t aWidth, uint32_t aHeight, uint32_t aStride,
                           uint32_t aInputFormat,
                           const nsAString& aOutputOptions) {
  NS_ENSURE_ARG(aData);
  nsresult rv;

  rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
  if (!NS_SUCCEEDED(rv)) {
    return rv;
  }

  rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride, aInputFormat,
                     aOutputOptions);
  if (!NS_SUCCEEDED(rv)) {
    return rv;
  }

  rv = EndImageEncode();

  return rv;
}

// nsPNGEncoder::StartImageEncode
//
//
// See ::InitFromData for other info.
NS_IMETHODIMP
nsPNGEncoder::StartImageEncode(uint32_t aWidth, uint32_t aHeight,
                               uint32_t aInputFormat,
                               const nsAString& aOutputOptions) {
  bool useTransparency = true, skipFirstFrame = false;
  uint32_t numFrames = 1;
  uint32_t numPlays = 0;  // For animations, 0 == forever
  int zlibLevel = DEFAULT_ZLIB_LEVEL;
  int filters = DEFAULT_FILTERS;

  // can't initialize more than once
  if (mImageBuffer != nullptr) {
    return NS_ERROR_ALREADY_INITIALIZED;
  }

  // validate input format
  if (aInputFormat != INPUT_FORMAT_RGB && aInputFormat != INPUT_FORMAT_RGBA &&
      aInputFormat != INPUT_FORMAT_HOSTARGB)
    return NS_ERROR_INVALID_ARG;

  // parse and check any provided output options
  nsresult rv = ParseOptions(aOutputOptions, &useTransparency, &skipFirstFrame,
                             &numFrames, &numPlays, &zlibLevel, &filters,
                             nullptr, nullptr, nullptr, nullptr, nullptr);
  if (rv != NS_OK) {
    return rv;
  }

#ifdef PNG_APNG_SUPPORTED
  if (numFrames > 1) {
    mIsAnimation = true;
  }

#endif

  // initialize
  mPNG = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, ErrorCallback,
                                 WarningCallback);
  if (!mPNG) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  mPNGinfo = png_create_info_struct(mPNG);
  if (!mPNGinfo) {
    png_destroy_write_struct(&mPNG, nullptr);
    return NS_ERROR_FAILURE;
  }

  // libpng's error handler jumps back here upon an error.
  // Note: It's important that all png_* callers do this, or errors
  // will result in a corrupt time-warped stack.
  if (setjmp(png_jmpbuf(mPNG))) {
    png_destroy_write_struct(&mPNG, &mPNGinfo);
    return NS_ERROR_FAILURE;
  }

#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
  png_set_compression_level(mPNG, zlibLevel);
#endif
#ifdef PNG_WRITE_FILTER_SUPPORTED
  png_set_filter(mPNG, PNG_FILTER_TYPE_BASE, filters);
#endif

  // Set up to read the data into our image buffer, start out with an 8K
  // estimated size. Note: we don't have to worry about freeing this data
  // in this function. It will be freed on object destruction.
  mImageBufferSize = 8192;
  mImageBuffer = (uint8_t*)malloc(mImageBufferSize);
  if (!mImageBuffer) {
    png_destroy_write_struct(&mPNG, &mPNGinfo);
    return NS_ERROR_OUT_OF_MEMORY;
  }
  mImageBufferUsed = 0;

  // set our callback for libpng to give us the data
  png_set_write_fn(mPNG, this, WriteCallback, nullptr);

  // include alpha?
  int colorType;
  if ((aInputFormat == INPUT_FORMAT_HOSTARGB ||
       aInputFormat == INPUT_FORMAT_RGBA) &&
      useTransparency)
    colorType = PNG_COLOR_TYPE_RGB_ALPHA;
  else
    colorType = PNG_COLOR_TYPE_RGB;

  png_set_IHDR(mPNG, mPNGinfo, aWidth, aHeight, 8, colorType,
               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
               PNG_FILTER_TYPE_DEFAULT);

#ifdef PNG_APNG_SUPPORTED
  if (mIsAnimation) {
    png_set_first_frame_is_hidden(mPNG, mPNGinfo, skipFirstFrame);
    png_set_acTL(mPNG, mPNGinfo, numFrames, numPlays);
  }
#endif

  // XXX: support PLTE, gAMA, tRNS, bKGD?

  png_write_info(mPNG, mPNGinfo);

  return NS_OK;
}

// Returns the number of bytes in the image buffer used.
NS_IMETHODIMP
nsPNGEncoder::GetImageBufferUsed(uint32_t* aOutputSize) {
  NS_ENSURE_ARG_POINTER(aOutputSize);
  *aOutputSize = mImageBufferUsed;
  return NS_OK;
}

// Returns a pointer to the start of the image buffer
NS_IMETHODIMP
nsPNGEncoder::GetImageBuffer(char** aOutputBuffer) {
  NS_ENSURE_ARG_POINTER(aOutputBuffer);
  *aOutputBuffer = reinterpret_cast<char*>(mImageBuffer);
  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::AddImageFrame(const uint8_t* aData,
                            uint32_t aLength,  // (unused, req'd by JS)
                            uint32_t aWidth, uint32_t aHeight, uint32_t aStride,
                            uint32_t aInputFormat,
                            const nsAString& aFrameOptions) {
  bool useTransparency = true;
  int filters = DEFAULT_FILTERS;
  uint32_t delay_ms = 500;
#ifdef PNG_APNG_SUPPORTED
  uint32_t dispose_op = PNG_DISPOSE_OP_NONE;
  uint32_t blend_op = PNG_BLEND_OP_SOURCE;
#else
  uint32_t dispose_op;
  uint32_t blend_op;
#endif
  uint32_t x_offset = 0, y_offset = 0;

  // must be initialized
  if (mImageBuffer == nullptr) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  // EndImageEncode was done, or some error occurred earlier
  if (!mPNG) {
    return NS_BASE_STREAM_CLOSED;
  }

  // validate input format
  if (aInputFormat != INPUT_FORMAT_RGB && aInputFormat != INPUT_FORMAT_RGBA &&
      aInputFormat != INPUT_FORMAT_HOSTARGB)
    return NS_ERROR_INVALID_ARG;

  // libpng's error handler jumps back here upon an error.
  if (setjmp(png_jmpbuf(mPNG))) {
    png_destroy_write_struct(&mPNG, &mPNGinfo);
    return NS_ERROR_FAILURE;
  }

  // parse and check any provided output options
  nsresult rv = ParseOptions(aFrameOptions, &useTransparency, nullptr, nullptr,
                             nullptr, nullptr, &filters, &dispose_op, &blend_op,
                             &delay_ms, &x_offset, &y_offset);
  if (rv != NS_OK) {
    return rv;
  }

#ifdef PNG_APNG_SUPPORTED
  if (mIsAnimation) {
    // XXX the row pointers arg (#3) is unused, can it be removed?
    png_write_frame_head(mPNG, mPNGinfo, nullptr, aWidth, aHeight, x_offset,
                         y_offset, delay_ms, 1000, dispose_op, blend_op);
  }
#endif

  // Stride is the padded width of each row, so it better be longer
  // (I'm afraid people will not understand what stride means, so
  // check it well)
  if ((aInputFormat == INPUT_FORMAT_RGB && aStride < aWidth * 3) ||
      ((aInputFormat == INPUT_FORMAT_RGBA ||
        aInputFormat == INPUT_FORMAT_HOSTARGB) &&
       aStride < aWidth * 4)) {
    NS_WARNING("Invalid stride for InitFromData/AddImageFrame");
    return NS_ERROR_INVALID_ARG;
  }

#ifdef PNG_WRITE_FILTER_SUPPORTED
  png_set_filter(mPNG, PNG_FILTER_TYPE_BASE, filters);
#endif

  // write each row: if we add more input formats, we may want to
  // generalize the conversions
  if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
    // PNG requires RGBA with post-multiplied alpha, so we need to
    // convert
    UniquePtr<uint8_t[]> row = MakeUniqueFallible<uint8_t[]>(aWidth * 4);
    if (NS_WARN_IF(!row)) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    for (uint32_t y = 0; y < aHeight; y++) {
      ConvertHostARGBRow(&aData[y * aStride], row.get(), aWidth,
                         useTransparency);
      png_write_row(mPNG, row.get());
    }
  } else if (aInputFormat == INPUT_FORMAT_RGBA && !useTransparency) {
    // RBGA, but we need to strip the alpha
    UniquePtr<uint8_t[]> row = MakeUniqueFallible<uint8_t[]>(aWidth * 4);
    if (NS_WARN_IF(!row)) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    for (uint32_t y = 0; y < aHeight; y++) {
      StripAlpha(&aData[y * aStride], row.get(), aWidth);
      png_write_row(mPNG, row.get());
    }
  } else if (aInputFormat == INPUT_FORMAT_RGB ||
             aInputFormat == INPUT_FORMAT_RGBA) {
    // simple RBG(A), no conversion needed
    for (uint32_t y = 0; y < aHeight; y++) {
      png_write_row(mPNG, (uint8_t*)&aData[y * aStride]);
    }

  } else {
    MOZ_ASSERT_UNREACHABLE("Bad format type");
    return NS_ERROR_INVALID_ARG;
  }

#ifdef PNG_APNG_SUPPORTED
  if (mIsAnimation) {
    png_write_frame_tail(mPNG, mPNGinfo);
  }
#endif

  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::EndImageEncode() {
  // must be initialized
  if (mImageBuffer == nullptr) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  // EndImageEncode has already been called, or some error
  // occurred earlier
  if (!mPNG) {
    return NS_BASE_STREAM_CLOSED;
  }

  // libpng's error handler jumps back here upon an error.
  if (setjmp(png_jmpbuf(mPNG))) {
    png_destroy_write_struct(&mPNG, &mPNGinfo);
    return NS_ERROR_FAILURE;
  }

  png_write_end(mPNG, mPNGinfo);
  png_destroy_write_struct(&mPNG, &mPNGinfo);

  mFinished = true;
  NotifyListener();

  // if output callback can't get enough memory, it will free our buffer
  if (!mImageBuffer) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  return NS_OK;
}

nsresult nsPNGEncoder::ParseOptions(const nsAString& aOptions,
                                    bool* useTransparency, bool* skipFirstFrame,
                                    uint32_t* numFrames, uint32_t* numPlays,
                                    int* zlibLevel, int* filters,
                                    uint32_t* frameDispose,
                                    uint32_t* frameBlend, uint32_t* frameDelay,
                                    uint32_t* offsetX, uint32_t* offsetY) {
#ifdef PNG_APNG_SUPPORTED
  // Make a copy of aOptions, because strtok() will modify it.
  nsAutoCString optionsCopy;
  optionsCopy.Assign(NS_ConvertUTF16toUTF8(aOptions));
  char* options = optionsCopy.BeginWriting();

  while (char* token = nsCRT::strtok(options, ";", &options)) {
    // If there's an '=' character, split the token around it.
    char* equals = token;
    char* value = nullptr;
    while (*equals != '=' && *equals) {
      ++equals;
    }
    if (*equals == '=') {
      value = equals + 1;
    }

    if (value) {
      *equals = '\0';  // temporary null
    }

    // transparency=[yes|no|none]
    if (nsCRT::strcmp(token, "transparency") == 0 && useTransparency) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (nsCRT::strcmp(value, "none") == 0 ||
          nsCRT::strcmp(value, "no") == 0) {
        *useTransparency = false;
      } else if (nsCRT::strcmp(value, "yes") == 0) {
        *useTransparency = true;
      } else {
        return NS_ERROR_INVALID_ARG;
      }

      // skipfirstframe=[yes|no]
    } else if (nsCRT::strcmp(token, "skipfirstframe") == 0 && skipFirstFrame) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (nsCRT::strcmp(value, "no") == 0) {
        *skipFirstFrame = false;
      } else if (nsCRT::strcmp(value, "yes") == 0) {
        *skipFirstFrame = true;
      } else {
        return NS_ERROR_INVALID_ARG;
      }

      // frames=#
    } else if (nsCRT::strcmp(token, "frames") == 0 && numFrames) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (PR_sscanf(value, "%u", numFrames) != 1) {
        return NS_ERROR_INVALID_ARG;
      }

      // frames=0 is nonsense.
      if (*numFrames == 0) {
        return NS_ERROR_INVALID_ARG;
      }

      // plays=#
    } else if (nsCRT::strcmp(token, "plays") == 0 && numPlays) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      // plays=0 to loop forever, otherwise play sequence specified
      // number of times
      if (PR_sscanf(value, "%u", numPlays) != 1) {
        return NS_ERROR_INVALID_ARG;
      }

      // png-zlib-level=#
    } else if (nsCRT::strcmp(token, "png-zlib-level") == 0) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      int localZlibLevel = DEFAULT_ZLIB_LEVEL;
      if (PR_sscanf(value, "%d", &localZlibLevel) != 1) {
        return NS_ERROR_INVALID_ARG;
      }

      // zlib-level 0-9 are the only valid values
      if (localZlibLevel < 0 || localZlibLevel > 9) {
        return NS_ERROR_INVALID_ARG;
      }

      if (zlibLevel) {
        *zlibLevel = localZlibLevel;
      }

      // png-filter=[no_filters|none|sub|up|avg|paeth|fast|all]
    } else if (nsCRT::strcmp(token, "png-filter") == 0) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (nsCRT::strcmp(value, "no_filters") == 0) {
        if (filters) {
          *filters = PNG_NO_FILTERS;
        }
      } else if (nsCRT::strcmp(value, "none") == 0) {
        if (filters) {
          *filters = PNG_FILTER_NONE;
        }
      } else if (nsCRT::strcmp(value, "sub") == 0) {
        if (filters) {
          *filters = PNG_FILTER_SUB;
        }
      } else if (nsCRT::strcmp(value, "up") == 0) {
        if (filters) {
          *filters = PNG_FILTER_UP;
        }
      } else if (nsCRT::strcmp(value, "avg") == 0) {
        if (filters) {
          *filters = PNG_FILTER_AVG;
        }
      } else if (nsCRT::strcmp(value, "paeth") == 0) {
        if (filters) {
          *filters = PNG_FILTER_PAETH;
        }
      } else if (nsCRT::strcmp(value, "fast") == 0) {
        if (filters) {
          *filters = PNG_FAST_FILTERS;
        }
      } else if (nsCRT::strcmp(value, "all") == 0) {
        if (filters) {
          *filters = PNG_ALL_FILTERS;
        }
      } else {
        return NS_ERROR_INVALID_ARG;
      }

      // dispose=[none|background|previous]
    } else if (nsCRT::strcmp(token, "dispose") == 0 && frameDispose) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (nsCRT::strcmp(value, "none") == 0) {
        *frameDispose = PNG_DISPOSE_OP_NONE;
      } else if (nsCRT::strcmp(value, "background") == 0) {
        *frameDispose = PNG_DISPOSE_OP_BACKGROUND;
      } else if (nsCRT::strcmp(value, "previous") == 0) {
        *frameDispose = PNG_DISPOSE_OP_PREVIOUS;
      } else {
        return NS_ERROR_INVALID_ARG;
      }

      // blend=[source|over]
    } else if (nsCRT::strcmp(token, "blend") == 0 && frameBlend) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (nsCRT::strcmp(value, "source") == 0) {
        *frameBlend = PNG_BLEND_OP_SOURCE;
      } else if (nsCRT::strcmp(value, "over") == 0) {
        *frameBlend = PNG_BLEND_OP_OVER;
      } else {
        return NS_ERROR_INVALID_ARG;
      }

      // delay=# (in ms)
    } else if (nsCRT::strcmp(token, "delay") == 0 && frameDelay) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (PR_sscanf(value, "%u", frameDelay) != 1) {
        return NS_ERROR_INVALID_ARG;
      }

      // xoffset=#
    } else if (nsCRT::strcmp(token, "xoffset") == 0 && offsetX) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (PR_sscanf(value, "%u", offsetX) != 1) {
        return NS_ERROR_INVALID_ARG;
      }

      // yoffset=#
    } else if (nsCRT::strcmp(token, "yoffset") == 0 && offsetY) {
      if (!value) {
        return NS_ERROR_INVALID_ARG;
      }

      if (PR_sscanf(value, "%u", offsetY) != 1) {
        return NS_ERROR_INVALID_ARG;
      }

      // unknown token name
    } else
      return NS_ERROR_INVALID_ARG;

    if (value) {
      *equals = '=';  // restore '=' so strtok doesn't get lost
    }
  }

#endif
  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::Close() {
  if (mImageBuffer != nullptr) {
    free(mImageBuffer);
    mImageBuffer = nullptr;
    mImageBufferSize = 0;
    mImageBufferUsed = 0;
    mImageBufferReadPoint = 0;
  }
  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::Available(uint64_t* _retval) {
  if (!mImageBuffer) {
    return NS_BASE_STREAM_CLOSED;
  }

  *_retval = mImageBufferUsed - mImageBufferReadPoint;
  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::StreamStatus() {
  return mImageBuffer ? NS_OK : NS_BASE_STREAM_CLOSED;
}

NS_IMETHODIMP
nsPNGEncoder::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
  return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
}

NS_IMETHODIMP
nsPNGEncoder::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                           uint32_t aCount, uint32_t* _retval) {
  // Avoid another thread reallocing the buffer underneath us
  ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);

  uint32_t maxCount = mImageBufferUsed - mImageBufferReadPoint;
  if (maxCount == 0) {
    *_retval = 0;
    return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
  }

  if (aCount > maxCount) {
    aCount = maxCount;
  }

  nsresult rv = aWriter(
      this, aClosure,
      reinterpret_cast<const char*>(mImageBuffer + mImageBufferReadPoint), 0,
      aCount, _retval);
  if (NS_SUCCEEDED(rv)) {
    NS_ASSERTION(*_retval <= aCount, "bad write count");
    mImageBufferReadPoint += *_retval;
  }

  // errors returned from the writer end here!
  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::IsNonBlocking(bool* _retval) {
  *_retval = true;
  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
                        uint32_t aRequestedCount, nsIEventTarget* aTarget) {
  if (aFlags != 0) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  if (mCallback || mCallbackTarget) {
    return NS_ERROR_UNEXPECTED;
  }

  mCallbackTarget = aTarget;
  // 0 means "any number of bytes except 0"
  mNotifyThreshold = aRequestedCount;
  if (!aRequestedCount) {
    mNotifyThreshold = 1024;  // We don't want to notify incessantly
  }

  // We set the callback absolutely last, because NotifyListener uses it to
  // determine if someone needs to be notified.  If we don't set it last,
  // NotifyListener might try to fire off a notification to a null target
  // which will generally cause non-threadsafe objects to be used off the main
  // thread
  mCallback = aCallback;

  // What we are being asked for may be present already
  NotifyListener();
  return NS_OK;
}

NS_IMETHODIMP
nsPNGEncoder::CloseWithStatus(nsresult aStatus) { return Close(); }

// nsPNGEncoder::ConvertHostARGBRow
//
//    Our colors are stored with premultiplied alphas, but PNGs use
//    post-multiplied alpha. This swaps to PNG-style alpha.
//
//    Copied from gfx/cairo/cairo/src/cairo-png.c

void nsPNGEncoder::ConvertHostARGBRow(const uint8_t* aSrc, uint8_t* aDest,
                                      uint32_t aPixelWidth,
                                      bool aUseTransparency) {
  uint32_t pixelStride = aUseTransparency ? 4 : 3;
  for (uint32_t x = 0; x < aPixelWidth; x++) {
    const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
    uint8_t* pixelOut = &aDest[x * pixelStride];

    uint8_t alpha = (pixelIn & 0xff000000) >> 24;
    pixelOut[pixelStride - 1] = alpha;  // overwritten below if pixelStride == 3
    if (alpha == 255) {
      pixelOut[0] = (pixelIn & 0xff0000) >> 16;
      pixelOut[1] = (pixelIn & 0x00ff00) >> 8;
      pixelOut[2] = (pixelIn & 0x0000ff);
    } else if (alpha == 0) {
      pixelOut[0] = pixelOut[1] = pixelOut[2] = 0;
    } else {
      pixelOut[0] = (((pixelIn & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
      pixelOut[1] = (((pixelIn & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
      pixelOut[2] = (((pixelIn & 0x0000ff)) * 255 + alpha / 2) / alpha;
    }
  }
}

// nsPNGEncoder::StripAlpha
//
//    Input is RGBA, output is RGB

void nsPNGEncoder::StripAlpha(const uint8_t* aSrc, uint8_t* aDest,
                              uint32_t aPixelWidth) {
  for (uint32_t x = 0; x < aPixelWidth; x++) {
    const uint8_t* pixelIn = &aSrc[x * 4];
    uint8_t* pixelOut = &aDest[x * 3];
    pixelOut[0] = pixelIn[0];
    pixelOut[1] = pixelIn[1];
    pixelOut[2] = pixelIn[2];
  }
}

// nsPNGEncoder::WarningCallback

void nsPNGEncoder::WarningCallback(png_structp png_ptr,
                                   png_const_charp warning_msg) {
  MOZ_LOG(sPNGEncoderLog, LogLevel::Warning,
          ("libpng warning: %s\n", warning_msg));
}

// nsPNGEncoder::ErrorCallback

void nsPNGEncoder::ErrorCallback(png_structp png_ptr,
                                 png_const_charp error_msg) {
  MOZ_LOG(sPNGEncoderLog, LogLevel::Error, ("libpng error: %s\n", error_msg));
  png_longjmp(png_ptr, 1);
}

// nsPNGEncoder::WriteCallback

void  // static
nsPNGEncoder::WriteCallback(png_structp png, png_bytep data, png_size_t size) {
  nsPNGEncoder* that = static_cast<nsPNGEncoder*>(png_get_io_ptr(png));
  if (!that->mImageBuffer) {
    return;
  }

  CheckedUint32 sizeNeeded = CheckedUint32(that->mImageBufferUsed) + size;
  if (!sizeNeeded.isValid()) {
    // Take the lock to ensure that nobody is trying to read from the buffer
    // we are destroying
    ReentrantMonitorAutoEnter autoEnter(that->mReentrantMonitor);

    that->NullOutImageBuffer();
    return;
  }

  if (sizeNeeded.value() > that->mImageBufferSize) {
    // When we're reallocing the buffer we need to take the lock to ensure
    // that nobody is trying to read from the buffer we are destroying
    ReentrantMonitorAutoEnter autoEnter(that->mReentrantMonitor);

    while (sizeNeeded.value() > that->mImageBufferSize) {
      // expand buffer, just double each time
      CheckedUint32 bufferSize = CheckedUint32(that->mImageBufferSize) * 2;
      if (!bufferSize.isValid()) {
        that->NullOutImageBuffer();
        return;
      }
      that->mImageBufferSize *= 2;
      uint8_t* newBuf =
          (uint8_t*)realloc(that->mImageBuffer, that->mImageBufferSize);
      if (!newBuf) {
        // can't resize, just zero (this will keep us from writing more)
        that->NullOutImageBuffer();
        return;
      }
      that->mImageBuffer = newBuf;
    }
  }

  memcpy(&that->mImageBuffer[that->mImageBufferUsed], data, size);
  that->mImageBufferUsed += size;
  that->NotifyListener();
}

void nsPNGEncoder::NullOutImageBuffer() {
  mReentrantMonitor.AssertCurrentThreadIn();

  free(mImageBuffer);
  mImageBuffer = nullptr;
  mImageBufferSize = 0;
  mImageBufferUsed = 0;
}

void nsPNGEncoder::NotifyListener() {
  // We might call this function on multiple threads (any threads that call
  // AsyncWait and any that do encoding) so we lock to avoid notifying the
  // listener twice about the same data (which generally leads to a truncated
  // image).
  ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);

  if (mCallback &&
      (mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
       mFinished)) {
    nsCOMPtr<nsIInputStreamCallback> callback;
    if (mCallbackTarget) {
      callback = NS_NewInputStreamReadyEvent("nsPNGEncoder::NotifyListener",
                                             mCallback, mCallbackTarget);
    } else {
      callback = mCallback;
    }

    NS_ASSERTION(callback, "Shouldn't fail to make the callback");
    // Null the callback first because OnInputStreamReady could reenter
    // AsyncWait
    mCallback = nullptr;
    mCallbackTarget = nullptr;
    mNotifyThreshold = 0;

    callback->OnInputStreamReady(this);
  }
}

Messung V0.5
C=89 H=96 G=92

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.