summaryrefslogtreecommitdiff
path: root/src/graphics/Bitmap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/Bitmap.cpp')
-rw-r--r--src/graphics/Bitmap.cpp1814
1 files changed, 1814 insertions, 0 deletions
diff --git a/src/graphics/Bitmap.cpp b/src/graphics/Bitmap.cpp
new file mode 100644
index 0000000..63b4115
--- /dev/null
+++ b/src/graphics/Bitmap.cpp
@@ -0,0 +1,1814 @@
+//
+// libavg - Media Playback Engine.
+// Copyright (C) 2003-2014 Ulrich von Zadow
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Current versions can be found at www.libavg.de
+//
+
+#include "Bitmap.h"
+#include "Pixel24.h"
+#include "Pixel16.h"
+#include "Pixel8.h"
+#include "Filter3x3.h"
+
+#include "../base/Exception.h"
+#include "../base/Logger.h"
+#include "../base/ObjectCounter.h"
+#include "../base/StringHelper.h"
+#include "../base/MathHelper.h"
+#include "../base/FileHelper.h"
+#include "../base/OSHelper.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <cstring>
+#include <iostream>
+#include <iomanip>
+#include <stdlib.h>
+
+using namespace std;
+
+namespace avg {
+
+template<class Pixel>
+void createTrueColorCopy(Bitmap& destBmp, const Bitmap & srcBmp);
+
+Bitmap::Bitmap(glm::vec2 size, PixelFormat pf, const UTF8String& sName, int stride)
+ : m_Size(size),
+ m_PF(pf),
+ m_pBits(0),
+ m_bOwnsBits(true),
+ m_sName(sName)
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+ allocBits(stride);
+}
+
+Bitmap::Bitmap(IntPoint size, PixelFormat pf, const UTF8String& sName, int stride)
+ : m_Size(size),
+ m_PF(pf),
+ m_pBits(0),
+ m_bOwnsBits(true),
+ m_sName(sName)
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+ allocBits(stride);
+}
+
+Bitmap::Bitmap(IntPoint size, PixelFormat pf, unsigned char* pBits,
+ int stride, bool bCopyBits, const UTF8String& sName)
+ : m_Size(size),
+ m_PF(pf),
+ m_pBits(0),
+ m_sName(sName)
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+ initWithData(pBits, stride, bCopyBits);
+}
+
+Bitmap::Bitmap(const Bitmap& origBmp)
+ : m_Size(origBmp.getSize()),
+ m_PF(origBmp.getPixelFormat()),
+ m_pBits(0),
+ m_bOwnsBits(origBmp.m_bOwnsBits),
+ m_sName(origBmp.getName()+" copy")
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+ initWithData(const_cast<unsigned char *>(origBmp.getPixels()), origBmp.getStride(),
+ m_bOwnsBits);
+}
+
+Bitmap::Bitmap(const Bitmap& origBmp, bool bOwnsBits)
+ : m_Size(origBmp.getSize()),
+ m_PF(origBmp.getPixelFormat()),
+ m_pBits(0),
+ m_bOwnsBits(bOwnsBits),
+ m_sName(origBmp.getName()+" copy")
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+ initWithData(const_cast<unsigned char *>(origBmp.getPixels()), origBmp.getStride(),
+ m_bOwnsBits);
+}
+
+// Creates a bitmap that is a rectangle in another bitmap. The pixels are
+// still owned by the original bitmap.
+Bitmap::Bitmap(Bitmap& origBmp, const IntRect& rect)
+ : m_Size(rect.size()),
+ m_PF(origBmp.getPixelFormat()),
+ m_pBits(0),
+ m_bOwnsBits(false)
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+ AVG_ASSERT(rect.br.x <= origBmp.getSize().x);
+ AVG_ASSERT(rect.br.y <= origBmp.getSize().y);
+ AVG_ASSERT(rect.tl.x >= 0 && rect.tl.y >= 0);
+ AVG_ASSERT(rect.width() > 0 && rect.height() > 0);
+ if (!origBmp.getName().empty()) {
+ m_sName = origBmp.getName()+" part";
+ } else {
+ m_sName = "";
+ }
+ unsigned char * pRegionStart = origBmp.getPixels()
+ + size_t(rect.tl.y)*origBmp.getStride() + rect.tl.x*getBytesPerPixel();
+ initWithData(pRegionStart, origBmp.getStride(), false);
+}
+
+Bitmap::~Bitmap()
+{
+ ObjectCounter::get()->decRef(&typeid(*this));
+ if (m_bOwnsBits) {
+ delete[] m_pBits;
+ m_pBits = 0;
+ }
+}
+
+Bitmap &Bitmap::operator =(const Bitmap& origBmp)
+{
+ if (this != &origBmp) {
+ if (m_bOwnsBits) {
+ delete[] m_pBits;
+ m_pBits = 0;
+ }
+ m_Size = origBmp.getSize();
+ m_PF = origBmp.getPixelFormat();
+ m_bOwnsBits = origBmp.m_bOwnsBits;
+ m_sName = origBmp.getName();
+ initWithData(const_cast<unsigned char *>(origBmp.getPixels()),
+ origBmp.getStride(), m_bOwnsBits);
+ }
+ return *this;
+}
+
+void Bitmap::copyPixels(const Bitmap& origBmp)
+{
+// cerr << "Bitmap::copyPixels(): " << getPixelFormatString(origBmp.getPixelFormat())
+// << "->" << getPixelFormatString(m_PF) << endl;
+ if (&origBmp == this || origBmp.getPixels() == m_pBits) {
+ return;
+ }
+ if (origBmp.getPixelFormat() == m_PF) {
+ const unsigned char * pSrc = origBmp.getPixels();
+ unsigned char * pDest = m_pBits;
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int lineLen = min(origBmp.getLineLen(), getLineLen());
+ int srcStride = origBmp.getStride();
+ for (int y = 0; y < height; ++y) {
+ memcpy(pDest, pSrc, lineLen);
+ pDest += m_Stride;
+ pSrc += srcStride;
+ }
+ } else {
+ switch (origBmp.getPixelFormat()) {
+ case YCbCr422:
+ case YUYV422:
+ case YCbCr411:
+ switch(m_PF) {
+ case B8G8R8X8:
+ YCbCrtoBGR(origBmp);
+ break;
+ case I8:
+ case A8:
+ YCbCrtoI8(origBmp);
+ default: {
+ Bitmap TempBmp(getSize(), B8G8R8X8, "TempColorConversion");
+ TempBmp.YCbCrtoBGR(origBmp);
+ copyPixels(TempBmp);
+ }
+ break;
+ }
+ break;
+ case I16:
+ if (m_PF == I8 || m_PF == A8) {
+ I16toI8(origBmp);
+ } else {
+ Bitmap TempBmp(getSize(), I8, "TempColorConversion");
+ TempBmp.I16toI8(origBmp);
+ copyPixels(TempBmp);
+ }
+ break;
+ case I8:
+ case A8:
+ switch(m_PF) {
+ case I16:
+ I8toI16(origBmp);
+ break;
+ case B8G8R8X8:
+ case B8G8R8A8:
+ case R8G8B8X8:
+ case R8G8B8A8:
+ case B8G8R8:
+ case R8G8B8:
+ I8toRGB(origBmp);
+ break;
+ default:
+ // Unimplemented conversion.
+ AVG_ASSERT(false);
+ }
+ break;
+ case BAYER8_RGGB:
+ case BAYER8_GBRG:
+ case BAYER8_GRBG:
+ case BAYER8_BGGR:
+ switch(m_PF) {
+ case I8:
+ case A8:
+ {
+ // Bayer patterns are saved as I8 bitmaps.
+ // So simply copy that.
+ const unsigned char * pSrc = origBmp.getPixels();
+ unsigned char * pDest = m_pBits;
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int lineLen = min(origBmp.getLineLen(), getLineLen());
+ int srcStride = origBmp.getStride();
+ for (int y = 0; y < height; ++y) {
+ memcpy(pDest, pSrc, lineLen);
+ pDest += m_Stride;
+ pSrc += srcStride;
+ }
+ }
+ break;
+ case B8G8R8X8:
+ case B8G8R8A8:
+ case R8G8B8X8:
+ case R8G8B8A8:
+ BY8toRGBBilinear(origBmp);
+ break;
+ default:
+ // Unimplemented conversion.
+ AVG_ASSERT(false);
+ }
+ break;
+ case R32G32B32A32F:
+ if (getBytesPerPixel() == 4) {
+ FloatRGBAtoByteRGBA(origBmp);
+ } else {
+ cerr << "Can't convert " << origBmp.getPixelFormat() << " to "
+ << getPixelFormat() << endl;
+ AVG_ASSERT(false);
+ }
+ break;
+ default:
+ switch(m_PF) {
+ case R32G32B32A32F:
+ if (origBmp.getBytesPerPixel() == 4) {
+ ByteRGBAtoFloatRGBA(origBmp);
+ } else {
+ cerr << "Can't convert " << origBmp.getPixelFormat() <<
+ " to " << getPixelFormat() << endl;
+ AVG_ASSERT(false);
+ }
+ break;
+ case B8G8R8A8:
+ case B8G8R8X8:
+ case A8B8G8R8:
+ case X8B8G8R8:
+ case R8G8B8A8:
+ case R8G8B8X8:
+ case A8R8G8B8:
+ case X8R8G8B8:
+ createTrueColorCopy<Pixel32>(*this, origBmp);
+ break;
+ case B8G8R8:
+ case R8G8B8:
+ createTrueColorCopy<Pixel24>(*this, origBmp);
+ break;
+ case B5G6R5:
+ case R5G6B5:
+ createTrueColorCopy<Pixel16>(*this, origBmp);
+ break;
+ case I8:
+ case A8:
+ createTrueColorCopy<Pixel8>(*this, origBmp);
+ break;
+ default:
+ // Unimplemented conversion.
+ cerr << "Can't convert " << origBmp.getPixelFormat() << " to " <<
+ getPixelFormat() << endl;
+ AVG_ASSERT(false);
+ }
+ }
+ }
+}
+
+#if defined(__SSE__) || defined(_WIN32)
+ostream& operator<<(ostream& os, const __m64 &val)
+{
+ unsigned char * pVal = (unsigned char *)(&val);
+ for (int i = 0; i < 8; ++i) {
+ os << hex << setw(2) << setfill('0') << int(pVal[i]);
+ if (i%2 == 1) {
+ os << " ";
+ }
+ if (i%4 == 3) {
+ os << " ";
+ }
+ }
+ return os;
+}
+#endif
+
+#define YUV_TO_RGB_UNPACK \
+ /* Input: r, g, b contain 4 words each of the u and v inputs for the color */ \
+ /* channels. ylo and yhi contain 4 words each of the y input. */ \
+\
+ /* duplicate u and v channels and add y \
+ * each of r,g, b in the form [s1(16), s2(16), s3(16), s4(16)] \
+ * first interleave, so tmp is [s1(16), s1(16), s2(16), s2(16)] \
+ * then add y, then interleave again \
+ * then pack with saturation, to get the desired output of \
+ * [s1(8), s1(8), s2(8), s2(8), s3(8), s3(8), s4(8), s4(8)] \
+ */ \
+ tmp = _m_punpckhwd(r, r); \
+ tmp = _m_paddsw(tmp, yhi); \
+ tmp2 = _m_punpcklwd(r, r); \
+ tmp2 = _m_paddsw(tmp2, ylo); \
+ r = _m_packuswb(tmp2, tmp); \
+ \
+ tmp = _m_punpckhwd(g, g); \
+ tmp2 = _m_punpcklwd(g, g); \
+ tmp = _m_paddsw(tmp, yhi); \
+ tmp2 = _m_paddsw(tmp2, ylo); \
+ g = _m_packuswb(tmp2, tmp); \
+ \
+ tmp = _m_punpckhwd(b, b); \
+ tmp2 = _m_punpcklwd(b, b); \
+ tmp = _m_paddsw(tmp, yhi); \
+ tmp2 = _m_paddsw(tmp2, ylo); \
+ b = _m_packuswb(tmp2, tmp); \
+ \
+ /* now we have 8 8-bit r, g and b samples. we want these to be packed \
+ * into 32-bit values. \
+ */ \
+ imm = _mm_set1_pi32(0xFFFFFFFF); \
+ tmp = _m_punpcklbw(b, r); \
+ tmp2 = _m_punpcklbw(g, imm); \
+ *o++ = _m_punpcklbw(tmp, tmp2); \
+ *o++ = _m_punpckhbw(tmp, tmp2); \
+ tmp = _m_punpckhbw(b, r); \
+ tmp2 = _m_punpckhbw(g, imm); \
+ *o++ = _m_punpcklbw(tmp, tmp2); \
+ *o++ = _m_punpckhbw(tmp, tmp2);
+
+
+void Bitmap::copyYUVPixels(const Bitmap& yBmp, const Bitmap& uBmp, const Bitmap& vBmp,
+ bool bJPEG)
+{
+ int height = min(yBmp.getSize().y, m_Size.y);
+ int width = min(yBmp.getSize().x, m_Size.x);
+
+ int yStride = yBmp.getStride();
+ int uStride = uBmp.getStride();
+ int vStride = vBmp.getStride();
+ int destStride = m_Stride/getBytesPerPixel();
+ Pixel32 * pDestLine = (Pixel32*)m_pBits;
+
+#if defined(__SSE__) || defined(_WIN32)
+#pragma pack(16)
+ // Original SSE conversion code taken from liboggplay: oggplay_sse_x86.c
+ int i;
+ const unsigned char * ptry;
+ const unsigned char * ptru;
+ const unsigned char * ptrv;
+
+ register __m64 *o;
+ register __m64 y, ylo, yhi;
+ register __m64 zero, ut, vt, imm;
+ register __m64 r, g, b;
+ register __m64 tmp, tmp2;
+
+ zero = _mm_setzero_si64();
+
+ ptry = yBmp.getPixels();
+ ptru = uBmp.getPixels();
+ ptrv = vBmp.getPixels();
+
+ for (i = 0; i < height; i++) {
+ int j;
+ o = (__m64*)pDestLine;
+ pDestLine += destStride;
+ if (bJPEG) {
+ for (j = 0; j < width; j += 8) {
+ // ylo and yhi contain 4 pixels each
+ y = *(__m64*)(&(ptry[j]));
+ ylo = _m_punpcklbw(y, zero);
+
+ yhi = _m_punpckhbw(y, zero);
+
+ ut = _m_from_int(*(int *)(ptru + j/2));
+ vt = _m_from_int(*(int *)(ptrv + j/2));
+
+ ut = _m_punpcklbw(ut, zero);
+ vt = _m_punpcklbw(vt, zero);
+
+ /* subtract 128 from u and v */
+ imm = _mm_set1_pi16(128);
+ ut = _m_psubw(ut, imm);
+ vt = _m_psubw(vt, imm);
+
+ /* transfer and multiply into r, g, b registers */
+ imm = _mm_set1_pi16(-44);
+ g = _m_pmullw(ut, imm);
+ imm = _mm_set1_pi16(113);
+ b = _m_pmullw(ut, imm);
+ imm = _mm_set1_pi16(179);
+ r = _m_pmullw(vt, imm);
+ imm = _mm_set1_pi16(-91);
+ imm = _m_pmullw(vt, imm);
+ g = _m_paddsw(g, imm);
+
+ /* shift r, g and b registers to the right */
+ r = _m_psrawi(r, 7);
+ g = _m_psrawi(g, 7);
+ b = _m_psrawi(b, 6);
+ YUV_TO_RGB_UNPACK
+
+ }
+ } else {
+ for (j = 0; j < width; j += 8) {
+
+ // y' = (298*(y-16))
+ // ylo and yhi contain 4 pixels each
+ y = *(__m64*)(&(ptry[j]));
+ ylo = _m_punpcklbw(y, zero);
+ imm = _mm_set1_pi16(16);
+ ylo = _m_psubusw(ylo, imm);
+ imm = _mm_set1_pi16(149);
+ ylo = _m_pmullw(ylo, imm);
+ ylo = _mm_srli_pi16(ylo, 7);
+
+ yhi = _m_punpckhbw(y, zero);
+ imm = _mm_set1_pi16(16);
+ yhi = _m_psubusw(yhi, imm);
+ imm = _mm_set1_pi16(149);
+ yhi = _m_pmullw(yhi, imm);
+ yhi = _mm_srli_pi16(yhi, 7);
+
+ ut = _m_from_int(*(int *)(ptru + j/2));
+ vt = _m_from_int(*(int *)(ptrv + j/2));
+
+ ut = _m_punpcklbw(ut, zero);
+ vt = _m_punpcklbw(vt, zero);
+
+ /* subtract 128 from u and v */
+ imm = _mm_set1_pi16(128);
+ ut = _m_psubw(ut, imm);
+ vt = _m_psubw(vt, imm);
+
+ /* transfer and multiply into r, g, b registers */
+ imm = _mm_set1_pi16(-50);
+ g = _m_pmullw(ut, imm);
+ imm = _mm_set1_pi16(129);
+ b = _m_pmullw(ut, imm);
+ imm = _mm_set1_pi16(204);
+ r = _m_pmullw(vt, imm);
+ imm = _mm_set1_pi16(-104);
+ imm = _m_pmullw(vt, imm);
+ g = _m_paddsw(g, imm);
+
+ /* shift r, g and b registers to the right */
+ r = _m_psrawi(r, 7);
+ g = _m_psrawi(g, 7);
+ b = _m_psrawi(b, 6);
+ YUV_TO_RGB_UNPACK
+ }
+ }
+ if (i & 0x1) {
+ ptru += uStride;
+ ptrv += vStride;
+ }
+ ptry += yStride;
+ }
+ _m_empty();
+#pragma pack()
+#else
+ AVG_ASSERT(m_PF==B8G8R8X8);
+ const unsigned char * pYSrc = yBmp.getPixels();
+ const unsigned char * pUSrc = uBmp.getPixels();
+ const unsigned char * pVSrc = vBmp.getPixels();
+
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ YUVtoBGR32Pixel(pDestLine + x, pYSrc[x], pUSrc[x/2], pVSrc[x/2]);
+ }
+ pDestLine += destStride;
+ pYSrc += yStride;
+ if (y % 2 == 1) {
+ pUSrc += uStride;
+ pVSrc += vStride;
+ }
+ }
+#endif
+}
+
+void Bitmap::save(const UTF8String& sFilename)
+{
+ Bitmap* pTempBmp;
+ switch (m_PF) {
+ case B8G8R8X8:
+ pTempBmp = new Bitmap(m_Size, R8G8B8);
+ for (size_t y = 0; y < size_t(m_Size.y); y++) {
+ unsigned char * pSrcLine = m_pBits + y*m_Stride;
+ unsigned char * pDestLine = pTempBmp->getPixels() +
+ y*pTempBmp->getStride();
+ for (size_t x = 0; x < size_t(m_Size.x); x++) {
+ pDestLine[x*3] = pSrcLine[x*4 + 2];
+ pDestLine[x*3 + 1] = pSrcLine[x*4 + 1];
+ pDestLine[x*3 + 2] = pSrcLine[x*4];
+ }
+ }
+ break;
+ case B8G8R8A8:
+ pTempBmp = new Bitmap(m_Size, R8G8B8A8);
+ for (size_t y = 0; y < size_t(m_Size.y); y++) {
+ unsigned char * pSrcLine = m_pBits+y * m_Stride;
+ unsigned char * pDestLine = pTempBmp->getPixels() +
+ y*pTempBmp->getStride();
+ for (size_t x = 0; x < size_t(m_Size.x); x++) {
+ pDestLine[x*4] = pSrcLine[x*4 + 2];
+ pDestLine[x*4 + 1] = pSrcLine[x*4 + 1];
+ pDestLine[x*4 + 2] = pSrcLine[x*4];
+ pDestLine[x*4 + 3] = pSrcLine[x*4+3];
+ }
+ }
+ break;
+ case B8G8R8:
+ pTempBmp = new Bitmap(m_Size, R8G8B8);
+ for (size_t y = 0; y < size_t(m_Size.y); y++) {
+ unsigned char * pSrcLine = m_pBits+y * m_Stride;
+ unsigned char * pDestLine = pTempBmp->getPixels() +
+ y*pTempBmp->getStride();
+ for (size_t x = 0; x < size_t(m_Size.x); x++) {
+ pDestLine[x*3] = pSrcLine[x*3 + 2];
+ pDestLine[x*3 + 1] = pSrcLine[x*3 + 1];
+ pDestLine[x*3 + 2] = pSrcLine[x*3];
+ }
+ }
+ break;
+ default:
+ if (hasAlpha()) {
+ pTempBmp = new Bitmap(m_Size, R8G8B8A8);
+ } else {
+ pTempBmp = new Bitmap(m_Size, R8G8B8);
+ }
+ pTempBmp->copyPixels(*this);
+ }
+ GdkPixbuf* pPixBuf = gdk_pixbuf_new_from_data(pTempBmp->getPixels(),
+ GDK_COLORSPACE_RGB, pTempBmp->hasAlpha(), 8, m_Size.x, m_Size.y,
+ pTempBmp->getStride(), 0, 0);
+
+ string sExt = getExtension(sFilename);
+ if (sExt == "jpg") {
+ sExt = "jpeg";
+ }
+
+ GError* pError = 0;
+ gboolean bOk = gdk_pixbuf_save(pPixBuf, sFilename.c_str(), sExt.c_str(), &pError,
+ NULL);
+ g_object_unref(pPixBuf);
+ if (!bOk) {
+ string sErr = pError->message;
+ g_error_free(pError);
+ throw Exception(AVG_ERR_FILEIO, sErr);
+ }
+
+ delete pTempBmp;
+}
+
+IntPoint Bitmap::getSize() const
+{
+ return m_Size;
+}
+
+int Bitmap::getStride() const
+{
+ return m_Stride;
+}
+
+PixelFormat Bitmap::getPixelFormat() const
+{
+ return m_PF;
+}
+
+void Bitmap::setPixelFormat(PixelFormat pf)
+{
+ m_PF = pf;
+}
+
+unsigned char * Bitmap::getPixels()
+{
+ return m_pBits;
+}
+
+const unsigned char * Bitmap::getPixels() const
+{
+ return m_pBits;
+}
+
+string Bitmap::getPixelsAsString() const
+{
+ return string((char*)m_pBits, getMemNeeded());
+}
+
+void Bitmap::setPixels(const unsigned char * pPixels)
+{
+ memcpy(m_pBits, pPixels, getMemNeeded());
+}
+
+void Bitmap::setPixelsFromString(const string& sPixels)
+{
+ memcpy(m_pBits, sPixels.c_str(), getMemNeeded());
+}
+
+
+const string& Bitmap::getName() const
+{
+ return m_sName;
+}
+
+bool Bitmap::ownsBits() const
+{
+ return m_bOwnsBits;
+}
+
+int Bitmap::getBytesPerPixel() const
+{
+ return avg::getBytesPerPixel(m_PF);
+}
+
+int Bitmap::getLineLen() const
+{
+ if (m_PF == YCbCr411) {
+ return int(m_Size.x*1.5);
+ } else {
+ return m_Size.x*getBytesPerPixel();
+ }
+}
+
+int Bitmap::getMemNeeded() const
+{
+ // This assumes a positive value for stride.
+ return m_Stride*m_Size.y;
+}
+
+bool Bitmap::hasAlpha() const
+{
+ return pixelFormatHasAlpha(m_PF);
+}
+
+HistogramPtr Bitmap::getHistogram(int stride) const
+{
+ AVG_ASSERT (getBytesPerPixel() == 1);
+ HistogramPtr pHist(new Histogram(256,0));
+ const unsigned char * pSrcLine = m_pBits;
+ for (int y = 0; y < m_Size.y; y += stride) {
+ const unsigned char * pSrc = pSrcLine;
+ for (int x = 0; x < m_Size.x; x += stride) {
+ (*pHist)[(*pSrc)]++;
+ pSrc += stride;
+ }
+ pSrcLine += m_Stride*stride;
+ }
+ return pHist;
+}
+
+void Bitmap::getMinMax(int stride, int& min, int& max) const
+{
+ AVG_ASSERT (getBytesPerPixel() == 1);
+ const unsigned char * pSrcLine = m_pBits;
+ min = 255;
+ max = 0;
+ for (int y = 0; y < m_Size.y; y += stride) {
+ const unsigned char * pSrc = pSrcLine;
+ for (int x = 0; x < m_Size.x; x += stride) {
+ if (*pSrc < min) {
+ min = *pSrc;
+ }
+ if (*pSrc > max) {
+ max = *pSrc;
+ }
+ pSrc += stride;
+ }
+ pSrcLine += m_Stride*stride;
+ }
+}
+
+void Bitmap::setAlpha(const Bitmap& alphaBmp)
+{
+ AVG_ASSERT(hasAlpha());
+ AVG_ASSERT(alphaBmp.getBytesPerPixel() == 1);
+ unsigned char * pLine = m_pBits;
+ const unsigned char * pAlphaLine = alphaBmp.getPixels();
+ for (int y = 0; y < m_Size.y; y++) {
+ unsigned char * pPixel = pLine;
+ const unsigned char * pAlphaPixel = pAlphaLine;
+ for (int x = 0; x < m_Size.x; x++) {
+ pPixel[ALPHAPOS] = *pAlphaPixel;
+ pPixel+=4;
+ pAlphaPixel++;
+ }
+ pLine += m_Stride;
+ pAlphaLine += alphaBmp.getStride();
+ }
+}
+
+Pixel32 Bitmap::getPythonPixel(const glm::vec2& pos)
+{
+ IntPoint intPos(pos);
+ if (intPos.x < 0 || intPos.y < 0 || intPos.x >= m_Size.x || intPos.y >= m_Size.y) {
+ stringstream ss;
+ ss << "Bitmap.getPixel(): intPos " << intPos <<
+ " is out of range. Bitmap size is " << m_Size << endl;
+ throw Exception(AVG_ERR_OUT_OF_RANGE, ss.str());
+ }
+ const unsigned char * pPixel = m_pBits+intPos.y*m_Stride+intPos.x*getBytesPerPixel();
+ switch(getPixelFormat()) {
+ case R8G8B8A8:
+ return Pixel32(pPixel[0], pPixel[1], pPixel[2], pPixel[3]);
+ case R8G8B8X8:
+ return Pixel32(pPixel[0], pPixel[1], pPixel[2], 255);
+ case R8G8B8:
+ return Pixel32(pPixel[0], pPixel[1], pPixel[2]);
+ case B8G8R8A8:
+ return Pixel32(pPixel[2], pPixel[1], pPixel[0], pPixel[3]);
+ case B8G8R8X8:
+ return Pixel32(pPixel[2], pPixel[1], pPixel[0], 255);
+ case B8G8R8:
+ return Pixel32(pPixel[2], pPixel[1], pPixel[0]);
+ case I8:
+ case A8:
+ return Pixel32(pPixel[0], pPixel[0], pPixel[0]);
+ default:
+ cerr << getPixelFormat() << endl;
+ AVG_ASSERT(false);
+ return Pixel32();
+ }
+}
+
+bool Bitmap::operator ==(const Bitmap& otherBmp)
+{
+ // We allow Name, Stride and bOwnsBits to be different here, since we're looking for
+ // equal value only.
+ if (m_Size != otherBmp.m_Size || m_PF != otherBmp.m_PF) {
+ return false;
+ }
+
+ const unsigned char * pSrc = otherBmp.getPixels();
+ unsigned char * pDest = m_pBits;
+ int lineLen = getLineLen();
+ for (int y = 0; y < getSize().y; ++y) {
+ switch(m_PF) {
+ case R8G8B8X8:
+ case B8G8R8X8:
+ for (int x = 0; x < getSize().x; ++x) {
+ const unsigned char * pSrcPixel = pSrc+x*getBytesPerPixel();
+ unsigned char * pDestPixel = pDest+x*getBytesPerPixel();
+ if (*((Pixel24*)(pDestPixel)) != *((Pixel24*)(pSrcPixel))) {
+ return false;
+ }
+ }
+ break;
+ default:
+ if (memcmp(pDest, pSrc, lineLen) != 0) {
+ return false;
+ }
+ }
+ pDest += m_Stride;
+ pSrc += otherBmp.getStride();
+ }
+ return true;
+}
+
+BitmapPtr Bitmap::subtract(const Bitmap& otherBmp)
+{
+ if (m_PF != otherBmp.getPixelFormat()) {
+ throw Exception(AVG_ERR_UNSUPPORTED,
+ string("Bitmap::subtract: pixel formats differ(")
+ + getPixelFormatString(m_PF)+", "
+ + getPixelFormatString(otherBmp.getPixelFormat())+")");
+ }
+ if (m_Size != otherBmp.getSize()) {
+ throw Exception(AVG_ERR_UNSUPPORTED,
+ string("Bitmap::subtract: bitmap sizes differ (this=")
+ + toString(m_Size) + ", other=" + toString(otherBmp.getSize()) + ")");
+ }
+ BitmapPtr pResultBmp = BitmapPtr(new Bitmap(m_Size, m_PF));
+ const unsigned char * pSrcLine1 = otherBmp.getPixels();
+ const unsigned char * pSrcLine2 = m_pBits;
+ unsigned char * pDestLine = pResultBmp->getPixels();
+ int stride = getStride();
+ int lineLen = getLineLen();
+
+ for (int y = 0; y < getSize().y; ++y) {
+ switch(m_PF) {
+ case I16:
+ {
+ const unsigned short * pSrc1 = (const unsigned short *)pSrcLine1;
+ const unsigned short * pSrc2 = (const unsigned short *)pSrcLine2;
+ unsigned short * pDest= (unsigned short *)pDestLine;
+ for (int x=0; x<m_Size.x; ++x) {
+ *pDest = abs(*pSrc1-*pSrc2);
+ pSrc1++;
+ pSrc2++;
+ pDest++;
+ }
+ }
+ break;
+ default:
+ {
+ const unsigned char * pSrc1 = pSrcLine1;
+ const unsigned char * pSrc2 = pSrcLine2;
+ unsigned char * pDest= pDestLine;
+ for (int x=0; x<lineLen; ++x) {
+ *pDest = abs(*pSrc1-*pSrc2);
+ pSrc1++;
+ pSrc2++;
+ pDest++;
+ }
+ }
+ }
+ pSrcLine1 += stride;
+ pSrcLine2 += stride;
+ pDestLine += stride;
+ }
+ return pResultBmp;
+}
+
+void Bitmap::blt(const Bitmap& otherBmp, const IntPoint& pos)
+{
+ AVG_ASSERT(getBytesPerPixel() == 4 || getBytesPerPixel() == 3);
+ AVG_ASSERT(otherBmp.getBytesPerPixel() == 4 || otherBmp.getBytesPerPixel() == 3);
+ if (pos.x < 0 || pos.y < 0) {
+ throw Exception(AVG_ERR_UNSUPPORTED,
+ string("Bitmap::blt: pos < 0 is not supported."));
+ }
+
+ IntRect destRect(pos.x, pos.y, pos.x+otherBmp.getSize().x,
+ pos.y+otherBmp.getSize().y);
+ destRect.intersect(IntRect(IntPoint(0,0), getSize()));
+ for (int y = 0; y < destRect.height(); y++) {
+ unsigned char * pSrcPixel = getPixels()+size_t(pos.y+y)*getStride()
+ +size_t(pos.x*getBytesPerPixel());
+ const unsigned char * pOtherPixel = otherBmp.getPixels()+
+ size_t(y*otherBmp.getStride());
+ if (getBytesPerPixel() == 4) {
+ if (otherBmp.hasAlpha()) {
+ for (int x = 0; x < destRect.width(); x++) {
+ int srcAlpha = 255-pOtherPixel[3];
+ pSrcPixel[0] =
+ (srcAlpha*pSrcPixel[0]+int(pOtherPixel[3])*pOtherPixel[0])/255;
+ pSrcPixel[1] =
+ (srcAlpha*pSrcPixel[1]+int(pOtherPixel[3])*pOtherPixel[1])/255;
+ pSrcPixel[2] =
+ (srcAlpha*pSrcPixel[2]+int(pOtherPixel[3])*pOtherPixel[2])/255;
+ pSrcPixel += 4;
+ pOtherPixel += 4;
+ }
+ } else {
+ for (int x = 0; x < destRect.width(); x++) {
+ *(Pixel32*)pSrcPixel = *(Pixel32*)pOtherPixel;
+ pSrcPixel[3] = 255;
+ pSrcPixel += 4;
+ pOtherPixel += 4;
+ }
+ }
+ } else {
+ if (otherBmp.getBytesPerPixel() == 4) {
+ // Incomplete: Missing alpha support.
+ for (int x = 0; x < destRect.width(); x++) {
+ *(Pixel24*)pSrcPixel = *(Pixel24*)pOtherPixel;
+ pSrcPixel += 3;
+ pOtherPixel += 4;
+ }
+ } else {
+ for (int x = 0; x < destRect.width(); x++) {
+ *(Pixel24*)pSrcPixel = *(Pixel24*)pOtherPixel;
+ pSrcPixel += 3;
+ pOtherPixel += 3;
+ }
+ }
+ }
+ }
+}
+
+float Bitmap::getAvg() const
+{
+ float sum = 0;
+ unsigned char * pSrc = m_pBits;
+ int componentsPerPixel = getBytesPerPixel();
+ for (int y = 0; y < getSize().y; ++y) {
+ switch(m_PF) {
+ case R8G8B8X8:
+ case B8G8R8X8:
+ {
+ Pixel32 * pSrcPixel = (Pixel32 *)pSrc;
+ for (int x = 0; x < m_Size.x; ++x) {
+ sum += pSrcPixel->getR()+pSrcPixel->getG()+pSrcPixel->getB();
+ pSrcPixel++;
+ }
+ componentsPerPixel = 3;
+ }
+ break;
+ case I16:
+ {
+ componentsPerPixel = 1;
+ unsigned short * pSrcPixel = (unsigned short *)pSrc;
+ for (int x = 0; x < m_Size.x; ++x) {
+ sum += *pSrcPixel;
+ pSrcPixel++;
+ }
+ }
+ break;
+ case R8G8B8A8:
+ case B8G8R8A8:
+ {
+ Pixel32 * pSrcPixel = (Pixel32 *)pSrc;
+ for (int x = 0; x < m_Size.x; ++x) {
+ int a = pSrcPixel->getA();
+ if (a > 0) {
+ sum += ((pSrcPixel->getR()+pSrcPixel->getG()+
+ pSrcPixel->getB())*a)/255+pSrcPixel->getA();
+ }
+ pSrcPixel++;
+ }
+ componentsPerPixel = 4;
+ }
+ break;
+ default:
+ {
+ unsigned char * pSrcComponent = pSrc;
+ for (int x = 0; x < getLineLen(); ++x) {
+ sum += *pSrcComponent;
+ pSrcComponent++;
+ }
+ }
+ }
+ pSrc += m_Stride;
+ }
+ sum /= componentsPerPixel;
+ return sum/(getSize().x*getSize().y);
+}
+
+float Bitmap::getChannelAvg(int channel) const
+{
+ AVG_ASSERT(!pixelFormatIsPlanar(m_PF) && !pixelFormatIsBayer(m_PF) && !(m_PF == I16));
+ int bytesPerPixel = getBytesPerPixel();
+ AVG_ASSERT(channel < bytesPerPixel);
+ float sum = 0;
+ unsigned char * pSrcLine = m_pBits;
+ for (int y = 0; y < getSize().y; ++y) {
+ unsigned char * pSrcPixel = pSrcLine;
+ for (int x = 0; x < m_Size.x; ++x) {
+ sum += *(pSrcPixel+channel);
+ pSrcPixel += bytesPerPixel;
+ }
+ pSrcLine += m_Stride;
+ }
+ return sum/(getSize().x*getSize().y);
+}
+
+float Bitmap::getStdDev() const
+{
+ float average = getAvg();
+ float sum = 0;
+
+ unsigned char * pSrc = m_pBits;
+ int componentsPerPixel = getBytesPerPixel();
+ for (int y = 0; y < getSize().y; ++y) {
+ switch(m_PF) {
+ case R8G8B8X8:
+ case B8G8R8X8:
+ {
+ componentsPerPixel = 3;
+ Pixel32 * pSrcPixel = (Pixel32 *)pSrc;
+ for (int x = 0; x < m_Size.x; ++x) {
+ sum += sqr(pSrcPixel->getR()-average);
+ sum += sqr(pSrcPixel->getG()-average);
+ sum += sqr(pSrcPixel->getB()-average);
+ pSrcPixel++;
+ }
+ }
+ break;
+ case R8G8B8A8:
+ case B8G8R8A8:
+ {
+ componentsPerPixel = 4;
+ Pixel32 * pSrcPixel = (Pixel32 *)pSrc;
+ for (int x = 0; x < m_Size.x; ++x) {
+ int a = pSrcPixel->getA();
+ if (a > 0) {
+ sum += sqr((pSrcPixel->getR()*a)/255-average);
+ sum += sqr((pSrcPixel->getG()*a)/255-average);
+ sum += sqr((pSrcPixel->getB()*a)/255-average);
+ sum += sqr(pSrcPixel->getA()-average);
+ }
+ pSrcPixel++;
+ }
+ }
+ break;
+ case I16:
+ {
+ componentsPerPixel = 1;
+ unsigned short * pSrcPixel = (unsigned short *)pSrc;
+ for (int x = 0; x < m_Size.x; ++x) {
+ sum += sqr(*pSrcPixel-average);
+ pSrcPixel++;
+ }
+ }
+ break;
+ default:
+ {
+ unsigned char * pSrcComponent = pSrc;
+ for (int x = 0; x < getLineLen(); ++x) {
+ sum += sqr(*pSrcComponent-average);
+ pSrcComponent++;
+ }
+ }
+ }
+ pSrc += m_Stride;
+ }
+ sum /= componentsPerPixel;
+ sum /= (getSize().x*getSize().y);
+ return sqrt(sum);
+}
+
+void Bitmap::dump(bool bDumpPixels) const
+{
+ cerr << "Bitmap: " << m_sName << endl;
+ cerr << " m_Size: " << m_Size.x << "x" << m_Size.y << endl;
+ cerr << " m_Stride: " << m_Stride << endl;
+ cerr << " m_PF: " << getPixelFormatString(m_PF) << endl;
+ cerr << " m_pBits: " << (void *)m_pBits << endl;
+ cerr << " m_bOwnsBits: " << m_bOwnsBits << endl;
+ IntPoint max;
+ if (bDumpPixels) {
+ max = m_Size;
+ } else {
+ max = IntPoint(16,1);
+ }
+ cerr << " Pixel data: " << endl;
+ for (int y = 0; y < max.y; ++y) {
+ unsigned char * pLine = m_pBits+m_Stride*y;
+ cerr << " ";
+ for (int x = 0; x < max.x; ++x) {
+ if (m_PF == R32G32B32A32F) {
+ float * pPixel = (float*)(pLine+getBytesPerPixel()*x);
+ cerr << "[";
+ for (int i = 0; i < 4; ++i) {
+ cerr << setw(4) << setprecision(2) << pPixel[i] << " ";
+ }
+ cerr << "]";
+ } else {
+ unsigned char * pPixel = pLine+getBytesPerPixel()*x;
+ cerr << "[";
+ for (int i = 0; i < getBytesPerPixel(); ++i) {
+ cerr << hex << setw(2) << (int)(pPixel[i]) << " ";
+ }
+ cerr << "]";
+ }
+ }
+ cerr << endl;
+ }
+ cerr << dec;
+}
+
+int Bitmap::getPreferredStride(int width, PixelFormat pf)
+{
+ return (((width*avg::getBytesPerPixel(pf))-1)/4+1)*4;
+}
+
+void Bitmap::initWithData(unsigned char * pBits, int stride, bool bCopyBits)
+{
+// cerr << "Bitmap::initWithData()" << endl;
+ if (m_PF == YCbCr422) {
+ if (m_Size.x%2 == 1) {
+ AVG_LOG_WARNING("Odd size for YCbCr bitmap.");
+ m_Size.x++;
+ }
+ if (m_Size.y%2 == 1) {
+ AVG_LOG_WARNING("Odd size for YCbCr bitmap.");
+ m_Size.y++;
+ }
+ if (m_Size.x%2 == 1 || m_Size.y%2 == 1) {
+ AVG_LOG_ERROR("Odd size for YCbCr bitmap.");
+ }
+ }
+ if (bCopyBits) {
+ allocBits();
+ if (m_Stride == stride && stride == (m_Size.x*getBytesPerPixel())) {
+ memcpy(m_pBits, pBits, stride*m_Size.y);
+ } else {
+ for (int y = 0; y < m_Size.y; ++y) {
+ memcpy(m_pBits+m_Stride*y, pBits+stride*y, m_Stride);
+ }
+ }
+ m_bOwnsBits = true;
+ } else {
+ m_pBits = pBits;
+ m_Stride = stride;
+ m_bOwnsBits = false;
+ }
+}
+
+void Bitmap::allocBits(int stride)
+{
+ AVG_ASSERT(!m_pBits);
+ AVG_ASSERT(!pixelFormatIsPlanar(m_PF));
+ AVG_ASSERT(m_Size.x > 0 && m_Size.y > 0);
+// cerr << "Bitmap::allocBits():" << m_Size << endl;
+ if (stride == 0) {
+ m_Stride = getPreferredStride(m_Size.x, m_PF);
+ } else {
+ m_Stride = stride;
+ }
+ if (m_PF == YCbCr422) {
+ if (m_Size.x%2 == 1) {
+ AVG_LOG_WARNING("Odd width for YCbCr bitmap.");
+ m_Size.x++;
+ }
+ if (m_Size.y%2 == 1) {
+ AVG_LOG_WARNING("Odd height for YCbCr bitmap.");
+ m_Size.y++;
+ }
+ //XXX: We allocate more than nessesary here because ffmpeg seems to
+ // overwrite memory after the bits - probably during yuv conversion.
+ // Yuck.
+ m_pBits = new unsigned char[size_t(m_Stride+1)*(m_Size.y+1)];
+ } else {
+ m_pBits = new unsigned char[size_t(m_Stride)*m_Size.y];
+ }
+}
+
+void YUYV422toBGR32Line(const unsigned char* pSrcLine, Pixel32* pDestLine, int width)
+{
+ Pixel32 * pDestPixel = pDestLine;
+
+ // We need the previous and next values to interpolate between the
+ // sampled u and v values.
+ int v = *(pSrcLine+3);
+ int v0; // Previous v
+ int u;
+ int u1; // Next u;
+ const unsigned char * pSrcPixels = pSrcLine;
+
+ for (int x = 0; x < width/2-1; x++) {
+ // Two pixels at a time.
+ // Source format is YUYV.
+ u = pSrcPixels[1];
+ v0 = v;
+ v = pSrcPixels[3];
+ u1 = pSrcPixels[5];
+
+ YUVtoBGR32Pixel(pDestPixel, pSrcPixels[0], u, (v0+v)/2);
+ YUVtoBGR32Pixel(pDestPixel+1, pSrcPixels[2], (u+u1)/2, v);
+
+ pSrcPixels+=4;
+ pDestPixel+=2;
+ }
+ // Last pixels.
+ u = pSrcPixels[1];
+ v0 = v;
+ v = pSrcPixels[3];
+ YUVtoBGR32Pixel(pDestPixel, pSrcPixels[0], u, v0/2+v/2);
+ YUVtoBGR32Pixel(pDestPixel+1, pSrcPixels[2], u, v);
+}
+
+void UYVY422toBGR32Line(const unsigned char* pSrcLine, Pixel32* pDestLine, int width)
+{
+ Pixel32 * pDestPixel = pDestLine;
+
+ // We need the previous and next values to interpolate between the
+ // sampled u and v values.
+ int v = *(pSrcLine+2);
+ int v0; // Previous v
+ int u;
+ int u1; // Next u;
+ const unsigned char * pSrcPixels = pSrcLine;
+
+ for (int x = 0; x < width/2-1; x++) {
+ // Two pixels at a time.
+ // Source format is UYVY.
+ u = pSrcPixels[0];
+ v0 = v;
+ v = pSrcPixels[2];
+ u1 = pSrcPixels[4];
+
+ YUVtoBGR32Pixel(pDestPixel, pSrcPixels[1], u, (v0+v)/2);
+ YUVtoBGR32Pixel(pDestPixel+1, pSrcPixels[3], (u+u1)/2, v);
+
+ pSrcPixels+=4;
+ pDestPixel+=2;
+ }
+ // Last pixels.
+ u = pSrcPixels[0];
+ v0 = v;
+ v = pSrcPixels[2];
+ YUVtoBGR32Pixel(pDestPixel, pSrcPixels[1], u, v0/2+v/2);
+ YUVtoBGR32Pixel(pDestPixel+1, pSrcPixels[3], u, v);
+}
+
+void YUV411toBGR32Line(const unsigned char* pSrcLine, Pixel32* pDestLine, int width)
+{
+ Pixel32 * pDestPixel = pDestLine;
+
+ // We need the previous and next values to interpolate between the
+ // sampled u and v values.
+ int v = *(pSrcLine+3);
+ int v0; // Previous v
+ int v1; // Next v;
+ int u;
+ int u1; // Next u;
+ const unsigned char * pSrcPixels = pSrcLine;
+
+ for (int x = 0; x < width/4; x++) {
+ // Four pixels at a time.
+ // Source format is UYYVYY.
+ u = pSrcPixels[0];
+ v0 = v;
+ v = pSrcPixels[3];
+
+ if (x < width/4-1) {
+ u1 = pSrcPixels[6];
+ v1 = pSrcPixels[9];
+ } else {
+ u1 = u;
+ v1 = v;
+ }
+
+ YUVtoBGR32Pixel(pDestPixel, pSrcPixels[1], u, v0/2+v/2);
+ YUVtoBGR32Pixel(pDestPixel+1, pSrcPixels[2], (u*3)/4+u1/4, v0/4+(v*3)/4);
+ YUVtoBGR32Pixel(pDestPixel+2, pSrcPixels[4], u/2+u1/2, v);
+ YUVtoBGR32Pixel(pDestPixel+3, pSrcPixels[5], u/4+(u1*3)/4, (v*3)/4+v1/4);
+
+ pSrcPixels+=6;
+ pDestPixel+=4;
+ }
+}
+
+void Bitmap::YCbCrtoBGR(const Bitmap& origBmp)
+{
+ AVG_ASSERT(m_PF==B8G8R8X8);
+ const unsigned char * pSrc = origBmp.getPixels();
+ Pixel32 * pDest = (Pixel32*)m_pBits;
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+ int StrideInPixels = m_Stride/getBytesPerPixel();
+ switch(origBmp.m_PF) {
+ case YCbCr422:
+ for (int y = 0; y < height; ++y) {
+ UYVY422toBGR32Line(pSrc, pDest, width);
+ pDest += StrideInPixels;
+ pSrc += origBmp.getStride();
+ }
+ break;
+ case YUYV422:
+ for (int y = 0; y < height; ++y) {
+ YUYV422toBGR32Line(pSrc, pDest, width);
+ pDest += StrideInPixels;
+ pSrc += origBmp.getStride();
+ }
+ break;
+ case YCbCr411:
+ for (int y = 0; y < height; ++y) {
+ YUV411toBGR32Line(pSrc, pDest, width);
+ pDest += StrideInPixels;
+ pSrc += origBmp.getStride();
+ }
+ break;
+ default:
+ // This routine shouldn't be called with other pixel formats.
+ AVG_ASSERT(false);
+ }
+}
+
+void YUYV422toI8Line(const unsigned char* pSrcLine, unsigned char* pDestLine, int width)
+{
+ const unsigned char * pSrc = pSrcLine;
+ unsigned char * pDest = pDestLine;
+ for (int x = 0; x < width; x++) {
+ *pDest = *pSrc;
+ pDest++;
+ pSrc+=2;
+ }
+}
+
+void YUV411toI8Line(const unsigned char* pSrcLine, unsigned char* pDestLine, int width)
+{
+ const unsigned char * pSrc = pSrcLine;
+ unsigned char * pDest = pDestLine;
+ for (int x = 0; x < width/2; x++) {
+ *pDest++ = *pSrc++;
+ *pDest++ = *pSrc++;
+ pSrc++;
+ }
+}
+
+void Bitmap::YCbCrtoI8(const Bitmap& origBmp)
+{
+ AVG_ASSERT(origBmp.getBytesPerPixel() == 1);
+ const unsigned char * pSrc = origBmp.getPixels();
+ unsigned char * pDest = m_pBits;
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+ switch(origBmp.m_PF) {
+ case YCbCr422:
+ for (int y = 0; y < height; ++y) {
+ // src shifted by one byte to account for UYVY to YUYV
+ // difference in pixel order.
+ YUYV422toI8Line(pSrc+1, pDest, width);
+ pDest += m_Stride;
+ pSrc += origBmp.getStride();
+ }
+ break;
+ case YUYV422:
+ for (int y = 0; y < height; ++y) {
+ YUYV422toI8Line(pSrc, pDest, width);
+ pDest += m_Stride;
+ pSrc += origBmp.getStride();
+ }
+ break;
+ case YCbCr411:
+ for (int y = 0; y < height; ++y) {
+ YUV411toI8Line(pSrc, pDest, width);
+ pDest += m_Stride;
+ pSrc += origBmp.getStride();
+ }
+ break;
+ default:
+ // This routine shouldn't be called with other pixel formats.
+ AVG_ASSERT(false);
+ }
+}
+
+void Bitmap::I16toI8(const Bitmap& origBmp)
+{
+ AVG_ASSERT(getBytesPerPixel() == 1);
+ AVG_ASSERT(origBmp.getPixelFormat() == I16);
+ const unsigned short * pSrc = (const unsigned short *)origBmp.getPixels();
+ unsigned char * pDest = m_pBits;
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+ int srcStrideInPixels = origBmp.getStride()/origBmp.getBytesPerPixel();
+ for (int y = 0; y < height; ++y) {
+ const unsigned short * pSrcPixel = pSrc;
+ unsigned char * pDestPixel = pDest;
+ for (int x=0; x<width; ++x) {
+ *pDestPixel++ = *pSrcPixel++ >> 8;
+ }
+ pDest += m_Stride;
+ pSrc += srcStrideInPixels;
+ }
+}
+
+void Bitmap::I8toI16(const Bitmap& origBmp)
+{
+ AVG_ASSERT(m_PF == I16);
+ AVG_ASSERT(origBmp.getBytesPerPixel() == 1);
+ const unsigned char * pSrc = origBmp.getPixels();
+ unsigned short * pDest = (unsigned short *)m_pBits;
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+ int destStrideInPixels = m_Stride/getBytesPerPixel();
+ for (int y=0; y<height; ++y) {
+ const unsigned char * pSrcPixel = pSrc;
+ unsigned short * pDestPixel = pDest;
+ for (int x=0; x<width; ++x) {
+ *pDestPixel++ = *pSrcPixel++ << 8;
+ }
+ pDest += destStrideInPixels;
+ pSrc += origBmp.getStride();
+ }
+}
+
+void Bitmap::I8toRGB(const Bitmap& origBmp)
+{
+ AVG_ASSERT(getBytesPerPixel() == 4 || getBytesPerPixel() == 3);
+ AVG_ASSERT(origBmp.getBytesPerPixel() == 1);
+ const unsigned char * pSrc = origBmp.getPixels();
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+ if (getBytesPerPixel() == 4) {
+ unsigned int * pDest = (unsigned int *)m_pBits;
+ int destStrideInPixels = m_Stride/getBytesPerPixel();
+ for (int y = 0; y < height; ++y) {
+ const unsigned char * pSrcPixel = pSrc;
+ unsigned int * pDestPixel = pDest;
+ for (int x = 0; x < width; ++x) {
+ *pDestPixel = (((((255 << 8)+(*pSrcPixel)) << 8)+
+ *pSrcPixel) << 8) +(*pSrcPixel);
+ pDestPixel ++;
+ pSrcPixel++;
+ }
+ pDest += destStrideInPixels;
+ pSrc += origBmp.getStride();
+ }
+ } else {
+ unsigned char * pDest = m_pBits;
+ for (int y = 0; y < height; ++y) {
+ const unsigned char * pSrcPixel = pSrc;
+ unsigned char * pDestPixel = pDest;
+ for (int x = 0; x < width; ++x) {
+ *pDestPixel++ = *pSrcPixel;
+ *pDestPixel++ = *pSrcPixel;
+ *pDestPixel++ = *pSrcPixel;
+ pSrcPixel++;
+ }
+ pDest += getStride();
+ pSrc += origBmp.getStride();
+ }
+ }
+}
+
+void Bitmap::ByteRGBAtoFloatRGBA(const Bitmap& origBmp)
+{
+ AVG_ASSERT(getPixelFormat() == R32G32B32A32F);
+ AVG_ASSERT(origBmp.getBytesPerPixel() == 4);
+ const unsigned char * pSrc = origBmp.getPixels();
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+ float * pDest = (float *)m_pBits;
+ for (int y = 0; y < height; ++y) {
+ const unsigned char * pSrcPixel = pSrc;
+ float * pDestPixel = pDest;
+ for (int x = 0; x < width*4; ++x) {
+ *pDestPixel = float(*pSrcPixel)/255;
+ pDestPixel ++;
+ pSrcPixel++;
+ }
+ pDest += m_Stride/sizeof(float);
+ pSrc += origBmp.getStride();
+ }
+}
+
+void Bitmap::FloatRGBAtoByteRGBA(const Bitmap& origBmp)
+{
+ AVG_ASSERT(getBytesPerPixel() == 4);
+ AVG_ASSERT(origBmp.getPixelFormat() == R32G32B32A32F);
+ const float * pSrc = (const float *)origBmp.getPixels();
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+ unsigned char * pDest = m_pBits;
+ for (int y = 0; y < height; ++y) {
+ const float * pSrcPixel = pSrc;
+ unsigned char * pDestPixel = pDest;
+ for (int x = 0; x < width*4; ++x) {
+ *pDestPixel = (unsigned char)(*pSrcPixel*255+0.5);
+ pDestPixel++;
+ pSrcPixel++;
+ }
+ pDest += m_Stride;
+ pSrc += origBmp.getStride()/sizeof(float);
+ }
+}
+
+/*
+// Nearest Neighbour Bayer Pattern de-mosaicking
+// Code has been taken and adapted from libdc1394 Bayer conversion
+// TODO: adapt it for RGB24, not just for RGB32
+// TODO: add more CFA patterns (now only the GBRG is defined and used)
+void Bitmap::BY8toRGBNearest(const Bitmap& origBmp)
+{
+ AVG_ASSERT(getBytesPerPixel() == 4);
+ AVG_ASSERT(origBmp.getPixelFormat() == BAYER8_GBRG);
+
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+
+ const int srcStride = width;
+ const int destStride = 4 * width;
+ int width = width;
+ int height = height;
+
+ // CFA Pattern selection: BGGR: blue=-1, swg=0; GRBG: blue=1, swg=1
+ // Assuming GBRG
+ int blue = 1;
+ int greenFirst = 1;
+
+ const unsigned char *pSrcPixel = origBmp.getPixels();
+ unsigned char *pDestPixel = (unsigned char *) getPixels();
+
+ pDestPixel += 1;
+ width -= 1;
+ height -= 1;
+
+ while (--height) {
+
+ const unsigned char *pSrcEndBoundary = pSrcPixel + width;
+
+ if (greenFirst) {
+ pDestPixel[-blue] = pSrcPixel[1];
+ pDestPixel[0] = pSrcPixel[srcStride + 1];
+ pDestPixel[blue] = pSrcPixel[srcStride];
+ pDestPixel[2] = 255; // Alpha channel
+ ++pSrcPixel;
+ pDestPixel += 4;
+ }
+
+ if (blue > 0) {
+ while (pSrcPixel <= pSrcEndBoundary - 2) {
+ pDestPixel[-1] = pSrcPixel[0];
+ pDestPixel[0] = pSrcPixel[1];
+ pDestPixel[1] = pSrcPixel[srcStride + 1];
+ pDestPixel[2] = 255; // Alpha channel
+
+ pDestPixel[3] = pSrcPixel[2];
+ pDestPixel[4] = pSrcPixel[srcStride + 2];
+ pDestPixel[5] = pSrcPixel[srcStride + 1];
+ pDestPixel[6] = 255; // Alpha channel
+
+ pSrcPixel += 2;
+ pDestPixel += 8;
+ }
+ } else {
+ while (pSrcPixel <= pSrcEndBoundary - 2) {
+ pDestPixel[1] = pSrcPixel[0];
+ pDestPixel[0] = pSrcPixel[1];
+ pDestPixel[-1] = pSrcPixel[srcStride + 1];
+
+ pDestPixel[6] = 255; // Alpha channel
+ pDestPixel[5] = pSrcPixel[2];
+ pDestPixel[4] = pSrcPixel[srcStride + 2];
+ pDestPixel[3] = pSrcPixel[srcStride + 1];
+ pDestPixel[2] = 255; // Alpha channel
+
+ pSrcPixel += 2;
+ pDestPixel += 8;
+ }
+ }
+
+ if (pSrcPixel < pSrcEndBoundary) {
+ pDestPixel[-blue] = pSrcPixel[0];
+ pDestPixel[0] = pSrcPixel[1];
+ pDestPixel[blue] = pSrcPixel[srcStride + 1];
+ pDestPixel[2] = 255; // Alpha channel
+ ++pSrcPixel;
+ pDestPixel += 4;
+ }
+
+ pSrcPixel -= width;
+ pDestPixel -= width * 4;
+
+ blue = -blue;
+ greenFirst = !greenFirst;
+
+ pSrcPixel += srcStride;
+ pDestPixel += destStride;
+ }
+}
+*/
+
+// Bilinear Bayer Pattern de-mosaicking
+// Code has been taken and adapted from libdc1394 Bayer conversion
+// Original source is OpenCV Bayer pattern decoding
+// TODO: adapt it for RGB24, not just for RGB32
+void Bitmap::BY8toRGBBilinear(const Bitmap& origBmp)
+{
+ AVG_ASSERT(getBytesPerPixel() == 4);
+ AVG_ASSERT(pixelFormatIsBayer(origBmp.getPixelFormat()));
+
+ int height = min(origBmp.getSize().y, m_Size.y);
+ int width = min(origBmp.getSize().x, m_Size.x);
+
+ const int srcStride = width;
+ const int doubleSrcStride = srcStride * 2;
+ const int destStride = 4 * width;
+
+ // CFA Pattern selection
+ PixelFormat pf = origBmp.getPixelFormat();
+ int blue;
+ int greenFirst;
+ if (pf == BAYER8_BGGR || pf == BAYER8_GBRG) {
+ blue = -1;
+ } else {
+ blue = 1;
+ }
+
+ if (pf == BAYER8_GBRG || pf == BAYER8_GRBG) {
+ greenFirst = 1;
+ } else {
+ greenFirst = 0;
+ }
+
+ const unsigned char *pSrcPixel = origBmp.getPixels();
+ unsigned char *pDestPixel = (unsigned char *) getPixels();
+
+ pDestPixel += destStride + 4 + 1;
+ height -= 2;
+ width -= 2;
+
+ while (height--) {
+ int t0, t1;
+ const unsigned char *pSrcEndBoundary = pSrcPixel + width;
+
+ if (greenFirst) {
+ t0 = (pSrcPixel[1] + pSrcPixel[doubleSrcStride + 1] + 1) >> 1;
+ t1 = (pSrcPixel[srcStride] + pSrcPixel[srcStride + 2] + 1) >> 1;
+ pDestPixel[-blue] = (unsigned char) t0;
+ pDestPixel[0] = pSrcPixel[srcStride + 1];
+ pDestPixel[blue] = (unsigned char) t1;
+ pDestPixel[2] = 255; // Alpha channel
+ ++pSrcPixel;
+ pDestPixel += 4;
+ }
+
+ if (blue > 0) {
+ while (pSrcPixel <= pSrcEndBoundary - 2) {
+ t0 = (pSrcPixel[0] + pSrcPixel[2] + pSrcPixel[doubleSrcStride] +
+ pSrcPixel[doubleSrcStride + 2] + 2) >> 2;
+ t1 = (pSrcPixel[1] + pSrcPixel[srcStride] +
+ pSrcPixel[srcStride + 2] + pSrcPixel[doubleSrcStride + 1] +
+ 2) >> 2;
+ pDestPixel[-1] = (unsigned char) t0;
+ pDestPixel[0] = (unsigned char) t1;
+ pDestPixel[1] = pSrcPixel[srcStride + 1];
+ pDestPixel[2] = 255; // Alpha channel
+
+ t0 = (pSrcPixel[2] + pSrcPixel[doubleSrcStride + 2] + 1) >> 1;
+ t1 = (pSrcPixel[srcStride + 1] + pSrcPixel[srcStride + 3] +
+ 1) >> 1;
+ pDestPixel[3] = (unsigned char) t0;
+ pDestPixel[4] = pSrcPixel[srcStride + 2];
+ pDestPixel[5] = (unsigned char) t1;
+ pDestPixel[6] = 255; // Alpha channel
+
+ pSrcPixel += 2;
+ pDestPixel += 8;
+ }
+ } else {
+ while (pSrcPixel <= pSrcEndBoundary - 2) {
+ t0 = (pSrcPixel[0] + pSrcPixel[2] + pSrcPixel[doubleSrcStride] +
+ pSrcPixel[doubleSrcStride + 2] + 2) >> 2;
+ t1 = (pSrcPixel[1] + pSrcPixel[srcStride] +
+ pSrcPixel[srcStride + 2] + pSrcPixel[doubleSrcStride + 1] +
+ 2) >> 2;
+ pDestPixel[1] = (unsigned char) t0;
+ pDestPixel[0] = (unsigned char) t1;
+ pDestPixel[-1] = pSrcPixel[srcStride + 1];
+ pDestPixel[2] = 255; // Alpha channel
+
+ t0 = (pSrcPixel[2] + pSrcPixel[doubleSrcStride + 2] + 1) >> 1;
+ t1 = (pSrcPixel[srcStride + 1] + pSrcPixel[srcStride + 3] +
+ 1) >> 1;
+ pDestPixel[5] = (unsigned char) t0;
+ pDestPixel[4] = pSrcPixel[srcStride + 2];
+ pDestPixel[3] = (unsigned char) t1;
+ pDestPixel[6] = 255; // Alpha channel
+
+ pSrcPixel += 2;
+ pDestPixel += 8;
+ }
+ }
+
+ if (pSrcPixel < pSrcEndBoundary) {
+ t0 = (pSrcPixel[0] + pSrcPixel[2] + pSrcPixel[doubleSrcStride] +
+ pSrcPixel[doubleSrcStride + 2] + 2) >> 2;
+ t1 = (pSrcPixel[1] + pSrcPixel[srcStride] +
+ pSrcPixel[srcStride + 2] + pSrcPixel[doubleSrcStride + 1] +
+ 2) >> 2;
+ pDestPixel[-blue] = (unsigned char) t0;
+ pDestPixel[0] = (unsigned char) t1;
+ pDestPixel[blue] = pSrcPixel[srcStride + 1];
+ pDestPixel[2] = 255; // Alpha channel
+ pSrcPixel++;
+ pDestPixel += 4;
+ }
+
+ pSrcPixel -= width;
+ pDestPixel -= width * 4;
+
+ blue = -blue;
+ greenFirst = !greenFirst;
+
+ pSrcPixel += srcStride;
+ pDestPixel += destStride;
+ }
+}
+
+template<class DESTPIXEL, class SRCPIXEL>
+void createTrueColorCopy(Bitmap& destBmp, const Bitmap& srcBmp)
+{
+ SRCPIXEL * pSrcLine = (SRCPIXEL*) srcBmp.getPixels();
+ DESTPIXEL * pDestLine = (DESTPIXEL*) destBmp.getPixels();
+ int height = min(srcBmp.getSize().y, destBmp.getSize().y);
+ int width = min(srcBmp.getSize().x, destBmp.getSize().x);
+ for (int y = 0; y < height; ++y) {
+ SRCPIXEL * pSrcPixel = pSrcLine;
+ DESTPIXEL * pDestPixel = pDestLine;
+ for (int x = 0; x < width; ++x) {
+ *pDestPixel = *pSrcPixel;
+ ++pSrcPixel;
+ ++pDestPixel;
+ }
+ pSrcLine = (SRCPIXEL *)((unsigned char *)pSrcLine + srcBmp.getStride());
+ pDestLine = (DESTPIXEL *)((unsigned char *)pDestLine + destBmp.getStride());
+ }
+}
+
+template<>
+void createTrueColorCopy<Pixel32, Pixel8>(Bitmap& destBmp, const Bitmap& srcBmp)
+{
+ const unsigned char * pSrcLine = srcBmp.getPixels();
+ unsigned char * pDestLine = destBmp.getPixels();
+ int height = min(srcBmp.getSize().y, destBmp.getSize().y);
+ int width = min(srcBmp.getSize().x, destBmp.getSize().x);
+ int srcStride = srcBmp.getStride();
+ int destStride = destBmp.getStride();
+ for (int y = 0; y < height; ++y) {
+ const unsigned char * pSrcPixel = pSrcLine;
+ unsigned char * pDestPixel = pDestLine;
+ for (int x = 0; x < width; ++x) {
+ pDestPixel[0] =
+ pDestPixel[1] =
+ pDestPixel[2] = *pSrcPixel;
+ pDestPixel[3] = 255;
+ ++pSrcPixel;
+ pDestPixel+=4;
+ }
+ pSrcLine = pSrcLine + srcStride;
+ pDestLine = pDestLine + destStride;
+ }
+}
+
+template<>
+void createTrueColorCopy<Pixel8, Pixel32>(Bitmap& destBmp, const Bitmap& srcBmp)
+{
+ const unsigned char * pSrcLine = srcBmp.getPixels();
+ unsigned char * pDestLine = destBmp.getPixels();
+ int height = min(srcBmp.getSize().y, destBmp.getSize().y);
+ int width = min(srcBmp.getSize().x, destBmp.getSize().x);
+ int srcStride = srcBmp.getStride();
+ int destStride = destBmp.getStride();
+ bool bRedFirst = (srcBmp.getPixelFormat() == R8G8B8A8) ||
+ (srcBmp.getPixelFormat() == R8G8B8X8);
+ for (int y = 0; y<height; ++y) {
+ const unsigned char * pSrcPixel = pSrcLine;
+ unsigned char * pDestPixel = pDestLine;
+ if (bRedFirst) {
+ for (int x = 0; x < width; ++x) {
+ *pDestPixel = ((pSrcPixel[0]*54+pSrcPixel[1]*183+pSrcPixel[2]*19)/256);
+ pSrcPixel+=4;
+ ++pDestPixel;
+ }
+ } else {
+ for (int x = 0; x < width; ++x) {
+ *pDestPixel = ((pSrcPixel[0]*19+pSrcPixel[1]*183+pSrcPixel[2]*54)/256);
+ pSrcPixel+=4;
+ ++pDestPixel;
+ }
+ }
+ pSrcLine = pSrcLine + srcStride;
+ pDestLine = pDestLine + destStride;
+ }
+}
+
+
+template<class PIXEL>
+void createTrueColorCopy(Bitmap& destBmp, const Bitmap& srcBmp)
+{
+ switch(srcBmp.getPixelFormat()) {
+ case B8G8R8A8:
+ case B8G8R8X8:
+ case A8B8G8R8:
+ case X8B8G8R8:
+ case R8G8B8A8:
+ case R8G8B8X8:
+ case A8R8G8B8:
+ case X8R8G8B8:
+ createTrueColorCopy<PIXEL, Pixel32>(destBmp, srcBmp);
+ break;
+ case B8G8R8:
+ case R8G8B8:
+ createTrueColorCopy<PIXEL, Pixel24>(destBmp, srcBmp);
+ break;
+ case B5G6R5:
+ case R5G6B5:
+ createTrueColorCopy<PIXEL, Pixel16>(destBmp, srcBmp);
+ break;
+ case I8:
+ case A8:
+ case BAYER8_RGGB:
+ case BAYER8_GBRG:
+ case BAYER8_GRBG:
+ case BAYER8_BGGR:
+ createTrueColorCopy<PIXEL, Pixel8>(destBmp, srcBmp);
+ break;
+ default:
+ // Unimplemented conversion.
+ AVG_ASSERT(false);
+ }
+}
+
+};