// -------------------------------------------------------------------- // Bitmaps // -------------------------------------------------------------------- /* This file is part of the extensible drawing editor Ipe. Copyright (C) 1993-2004 Otfried Cheong Ipe is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. As a special exception, you have permission to link Ipe with the CGAL library and distribute executables, as long as you follow the requirements of the Gnu General Public License in regard to all of the software in the executable aside from CGAL. Ipe 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Ipe; if not, you can find it at "http://www.gnu.org/copyleft/gpl.html", or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ipebitmap.h" #include "ipeutils.h" #include extern bool dctDecode(IpeBuffer dctData, IpeBuffer pixelData); // -------------------------------------------------------------------- /*! \class IpeBitmap::MRenderData \ingroup base \brief Abstract base class for pixmap data stored by a client. */ IpeBitmap::MRenderData::~MRenderData() { // nothing } /*! \class IpeBitmap \ingroup base \brief A bitmap. Bitmaps are explicitely shared using reference-counting. Copying is cheap, so IpeBitmap objects are meant to be passed by value. The bitmap can cache data to speed up rendering. This data can be set only once (as the bitmap is conceptually immutable). The bitmap also provides a slot for short-term storage of an "object number". The PDF embedder, for instance, sets it to the PDF object number when embedding the bitmap, and can reuse it when "drawing" the bitmap. */ //! Default constructor constructs null bitmap. IpeBitmap::IpeBitmap() { iImp = 0; } //! Create from XML stream. IpeBitmap::IpeBitmap(const IpeXmlAttributes &attr, IpeString data) { int length = Init(attr); // decode data iImp->iData = IpeBuffer(length); char *p = iImp->iData.data(); IpeLex datalex(data); while (length-- > 0) *p++ = char(datalex.GetHexByte()); ComputeChecksum(); } //! Create from XML using external raw data IpeBitmap::IpeBitmap(const IpeXmlAttributes &attr, IpeBuffer data) { int length = Init(attr); assert(length == data.size()); iImp->iData = data; ComputeChecksum(); } int IpeBitmap::Init(const IpeXmlAttributes &attr) { iImp = new Imp; iImp->iRefCount = 1; iImp->iObjNum = IpeLex(attr["id"]).GetInt(); iImp->iRender = 0; iImp->iWidth = IpeLex(attr["width"]).GetInt(); iImp->iHeight = IpeLex(attr["height"]).GetInt(); int length = IpeLex(attr["length"]).GetInt(); assert(iImp->iWidth > 0 && iImp->iHeight > 0); IpeString cs = attr["ColorSpace"]; if (cs == "DeviceGray") { iImp->iComponents = 1; iImp->iColorSpace = EDeviceGray; } else if (cs == "DeviceCMYK") { iImp->iComponents = 4; iImp->iColorSpace = EDeviceCMYK; } else { iImp->iComponents = 3; iImp->iColorSpace = EDeviceRGB; } IpeString fi = attr["Filter"]; if (fi == "DCTDecode") iImp->iFilter = EDCTDecode; else if (fi == "FlateDecode") iImp->iFilter = EFlateDecode; else iImp->iFilter = EDirect; iImp->iBitsPerComponent = IpeLex(attr["BitsPerComponent"]).GetInt(); if (length == 0) { assert(iImp->iFilter == EDirect); int bitsPerRow = Width() * Components() * BitsPerComponent(); int bytesPerRow = (bitsPerRow + 7) / 8; length = Height() * bytesPerRow; } return length; } //! Create a new image IpeBitmap::IpeBitmap(int width, int height, TColorSpace colorSpace, int bitsPerComponent, IpeBuffer data, TFilter filter, bool deflate) { iImp = new Imp; iImp->iRefCount = 1; iImp->iObjNum = -1; iImp->iRender = 0; iImp->iWidth = width; iImp->iHeight = height; assert(iImp->iWidth > 0 && iImp->iHeight > 0); iImp->iColorSpace = colorSpace; switch (colorSpace) { case EDeviceGray: iImp->iComponents = 1; break; case EDeviceRGB: iImp->iComponents = 3; break; case EDeviceCMYK: iImp->iComponents = 4; break; } iImp->iFilter = filter; iImp->iBitsPerComponent = bitsPerComponent; if (deflate && filter == EDirect) { // optional deflation int deflatedSize; IpeBuffer deflated = IpeDeflateStream::Deflate(data.data(), data.size(), deflatedSize, 9); iImp->iData = IpeBuffer(deflated.data(), deflatedSize); iImp->iFilter = EFlateDecode; } else iImp->iData = data; ComputeChecksum(); } //! Copy constructor. IpeBitmap::IpeBitmap(const IpeBitmap &rhs) { iImp = rhs.iImp; if (iImp) iImp->iRefCount++; } //! Destructor. IpeBitmap::~IpeBitmap() { if (iImp && --iImp->iRefCount == 0) { delete iImp->iRender; delete iImp; } } //! Assignment operator (takes care of reference counting). IpeBitmap &IpeBitmap::operator=(const IpeBitmap &rhs) { if (this != &rhs) { if (iImp && --iImp->iRefCount == 0) delete iImp; iImp = rhs.iImp; if (iImp) iImp->iRefCount++; } return *this; } //! Save bitmap in XML stream. void IpeBitmap::SaveAsXml(IpeStream &stream, int id, int pdfObjNum) const { assert(iImp); stream << "= 0) { stream << " pdfObject=\"" << pdfObjNum << "\"/>\n"; } else { // save data stream << ">\n"; const char *data = Data(); const char *fin = data + Size(); int col = 0; while (data != fin) { stream.PutHexByte(*data++); if (++col == 36) { stream << "\n"; col = 0; } } if (col > 0) stream << "\n"; stream << "\n"; } } //! Set a cached bitmap for fast rendering. void IpeBitmap::SetRenderData(MRenderData *data) const { assert(iImp && iImp->iRender == 0); iImp->iRender = data; } bool IpeBitmap::Equal(IpeBitmap rhs) const { if (iImp == rhs.iImp) return true; if (!iImp || !rhs.iImp) return false; if (iImp->iColorSpace != rhs.iImp->iColorSpace || iImp->iBitsPerComponent != rhs.iImp->iBitsPerComponent || iImp->iWidth != rhs.iImp->iWidth || iImp->iHeight != rhs.iImp->iHeight || iImp->iComponents != rhs.iImp->iComponents || iImp->iFilter != rhs.iImp->iFilter || iImp->iChecksum != rhs.iImp->iChecksum || iImp->iData.size() != rhs.iImp->iData.size()) return false; // check actual data int len = iImp->iData.size(); char *p = iImp->iData.data(); char *q = rhs.iImp->iData.data(); while (len--) { if (*p++ != *q++) return false; } return true; } void IpeBitmap::ComputeChecksum() { int s = 0; int len = iImp->iData.size(); char *p = iImp->iData.data(); while (len--) { s = (s & 0x0fffffff) << 3; s += *p++; } iImp->iChecksum = s; } // -------------------------------------------------------------------- #if 0 static inline double clip01(double x) { return (x < 0) ? 0 : ((x > 1) ? 1 : x); } // CMYK to RGB void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { double c, m, y, aw, ac, am, ay, ar, ag, ab; c = clip01(color->c[0] + color->c[3]); m = clip01(color->c[1] + color->c[3]); y = clip01(color->c[2] + color->c[3]); aw = (1-c) * (1-m) * (1-y); ac = c * (1-m) * (1-y); am = (1-c) * m * (1-y); ay = (1-c) * (1-m) * y; ar = (1-c) * m * y; ag = c * (1-m) * y; ab = c * m * (1-y); rgb->r = clip01(aw + 0.9137*am + 0.9961*ay + 0.9882*ar); rgb->g = clip01(aw + 0.6196*ac + ay + 0.5176*ag); rgb->b = clip01(aw + 0.7804*ac + 0.5412*am + 0.0667*ar + 0.2118*ag + 0.4863*ab); } #endif //! Convert bitmap data to a height x width pixel array in rgb format. /*! Returns empty buffer if it cannot decode the bitmap information. Otherwise, returns a buffer of size Width() * Height() uint's. */ IpeBuffer IpeBitmap::PixelData() const { ipeDebug("PixelData %d x %d x %d, %d", Width(), Height(), Components(), int(Filter())); if (BitsPerComponent() != 8) return IpeBuffer(); IpeBuffer stream = iImp->iData; IpeBuffer pixels; if (Filter() == EDirect) { pixels = stream; } else if (Filter() == EFlateDecode) { // inflate data uLong inflatedSize = Width() * Height() * Components(); pixels = IpeBuffer(inflatedSize); if (uncompress((Bytef *) pixels.data(), &inflatedSize, (const Bytef *) stream.data(), stream.size()) != Z_OK || pixels.size() != int(inflatedSize)) return IpeBuffer(); } else if (Filter() == EDCTDecode) { pixels = IpeBuffer(Width() * Height() * Components()); if (!dctDecode(stream, pixels)) return IpeBuffer(); } IpeBuffer data(Height() * Width() * sizeof(uint)); // convert pixels to data const char *p = pixels.data(); uint *q = (uint *) data.data(); if (Components() == 3) { for (int y = 0; y < iImp->iHeight; ++y) { for (int x = 0; x < iImp->iWidth; ++x) { uchar r = uchar(*p++); uchar g = uchar(*p++); uchar b = uchar(*p++); *q++ = (r << 16) | (g << 8) | b; } } } else if (Components() == 1) { for (int y = 0; y < iImp->iHeight; ++y) { for (int x = 0; x < iImp->iWidth; ++x) { uchar r = uchar(*p++); *q++ = (r << 16) | (r << 8) | r; } } } return data; } // --------------------------------------------------------------------