/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include class FilterConfigItem; #define RAS_TYPE_OLD 0x00000000 // supported formats by this filter #define RAS_TYPE_STANDARD 0x00000001 #define RAS_TYPE_BYTE_ENCODED 0x00000002 #define RAS_TYPE_RGB_FORMAT 0x00000003 #define RAS_COLOR_NO_MAP 0x00000000 #define RAS_COLOR_RGB_MAP 0x00000001 #define RAS_COLOR_RAW_MAP 0x00000002 #define SUNRASTER_MAGICNUMBER 0x59a66a95 //============================ RASReader ================================== namespace { class RASReader { private: SvStream& m_rRAS; // the RAS file to be read in bool mbStatus; sal_Int32 mnWidth, mnHeight; // image dimensions in pixels sal_uInt16 mnDstBitsPerPix; sal_uInt16 mnDstColors; sal_Int32 mnDepth, mnImageDatSize, mnType; sal_Int32 mnColorMapType, mnColorMapSize; sal_uInt8 mnRepCount, mnRepVal; // RLE Decoding bool ImplReadBody(vcl::bitmap::RawBitmap&, std::vector const & rvPalette); bool ImplReadHeader(); sal_uInt8 ImplGetByte(); public: explicit RASReader(SvStream &rRAS); bool ReadRAS(Graphic & rGraphic); }; } //=================== Methods of RASReader ============================== RASReader::RASReader(SvStream &rRAS) : m_rRAS(rRAS) , mbStatus(true) , mnWidth(0) , mnHeight(0) , mnDstBitsPerPix(0) , mnDstColors(0) , mnDepth(0) , mnImageDatSize(0) , mnType(0) , mnColorMapType(0) , mnColorMapSize(0) , mnRepCount(0) , mnRepVal(0) { } bool RASReader::ReadRAS(Graphic & rGraphic) { sal_uInt32 nMagicNumber; if ( m_rRAS.GetError() ) return false; m_rRAS.SetEndian( SvStreamEndian::BIG ); m_rRAS.ReadUInt32( nMagicNumber ); if (!m_rRAS.good() || nMagicNumber != SUNRASTER_MAGICNUMBER) return false; // Kopf einlesen: mbStatus = ImplReadHeader(); if ( !mbStatus ) return false; std::vector aPalette; bool bOk = true; if ( mnDstBitsPerPix <= 8 ) // pallets pictures { bool bPalette(false); if ( mnColorMapType == RAS_COLOR_RAW_MAP ) // RAW color map is skipped { sal_uInt64 nCurPos = m_rRAS.Tell(); bOk = checkSeek(m_rRAS, nCurPos + mnColorMapSize); } else if ( mnColorMapType == RAS_COLOR_RGB_MAP ) // we can read out the RGB { mnDstColors = static_cast( mnColorMapSize / 3 ); if ( ( 1 << mnDstBitsPerPix ) < mnDstColors ) return false; if ( ( mnDstColors >= 2 ) && ( ( mnColorMapSize % 3 ) == 0 ) ) { aPalette.resize(mnDstColors); sal_uInt16 i; sal_uInt8 nRed[256], nGreen[256], nBlue[256]; for ( i = 0; i < mnDstColors; i++ ) m_rRAS.ReadUChar( nRed[ i ] ); for ( i = 0; i < mnDstColors; i++ ) m_rRAS.ReadUChar( nGreen[ i ] ); for ( i = 0; i < mnDstColors; i++ ) m_rRAS.ReadUChar( nBlue[ i ] ); for ( i = 0; i < mnDstColors; i++ ) { aPalette[i] = Color(nRed[ i ], nGreen[ i ], nBlue[ i ]); } bPalette = true; } else return false; } else if ( mnColorMapType != RAS_COLOR_NO_MAP ) // everything else is not standard return false; if (!bPalette) { mnDstColors = 1 << mnDstBitsPerPix; aPalette.resize(mnDstColors); for ( sal_uInt16 i = 0; i < mnDstColors; i++ ) { sal_uInt8 nCount = 255 - ( 255 * i / ( mnDstColors - 1 ) ); aPalette[i] = Color(nCount, nCount, nCount); } } } else { if ( mnColorMapType != RAS_COLOR_NO_MAP ) // when graphic has more than 256 colors and a color map we skip { // the colormap sal_uInt64 nCurPos = m_rRAS.Tell(); bOk = checkSeek(m_rRAS, nCurPos + mnColorMapSize); } } if (!bOk) return false; //The RLE packets are typically three bytes in size: //The first byte is a Flag Value indicating the type of RLE packet. //The second byte is the Run Count. //The third byte is the Run Value. // //for the sake of simplicity we'll assume that RAS_TYPE_BYTE_ENCODED can //describe data 255 times larger than the data stored size_t nMaxCompression = mnType != RAS_TYPE_BYTE_ENCODED ? 1 : 255; sal_Int32 nBitSize; if (o3tl::checked_multiply(mnWidth, mnHeight, nBitSize) || o3tl::checked_multiply(nBitSize, mnDepth, nBitSize)) return false; if (m_rRAS.remainingSize() * nMaxCompression < static_cast(nBitSize) / 8) return false; vcl::bitmap::RawBitmap aBmp(Size(mnWidth, mnHeight), 24); // read in the bitmap data mbStatus = ImplReadBody(aBmp, aPalette); if ( mbStatus ) rGraphic = vcl::bitmap::CreateFromData(std::move(aBmp)); return mbStatus; } bool RASReader::ImplReadHeader() { m_rRAS.ReadInt32(mnWidth).ReadInt32(mnHeight).ReadInt32(mnDepth).ReadInt32(mnImageDatSize).ReadInt32(mnType).ReadInt32(mnColorMapType).ReadInt32(mnColorMapSize); if (!m_rRAS.good() || mnWidth <= 0 || mnHeight <= 0 || mnImageDatSize <= 0) mbStatus = false; switch ( mnDepth ) { case 24 : case 8 : case 1 : mnDstBitsPerPix = static_cast(mnDepth); break; case 32 : mnDstBitsPerPix = 24; break; default : mbStatus = false; } switch ( mnType ) { case RAS_TYPE_OLD : case RAS_TYPE_STANDARD : case RAS_TYPE_RGB_FORMAT : case RAS_TYPE_BYTE_ENCODED : // this type will be supported later break; default: mbStatus = false; } return mbStatus; } namespace { const Color& SanitizePaletteIndex(std::vector const & rvPalette, sal_uInt8 nIndex) { if (nIndex >= rvPalette.size()) { auto nSanitizedIndex = nIndex % rvPalette.size(); SAL_WARN_IF(nIndex != nSanitizedIndex, "filter.ras", "invalid colormap index: " << static_cast(nIndex) << ", colormap len is: " << rvPalette.size()); nIndex = nSanitizedIndex; } return rvPalette[nIndex]; } } bool RASReader::ImplReadBody(vcl::bitmap::RawBitmap& rBitmap, std::vector const & rvPalette) { sal_Int32 x, y; sal_uInt8 nRed, nGreen, nBlue; switch ( mnDstBitsPerPix ) { case 1 : { sal_uInt8 nDat = 0; for (y = 0; y < mnHeight && mbStatus; ++y) { for (x = 0; x < mnWidth && mbStatus; ++x) { if (!(x & 7)) { nDat = ImplGetByte(); if (!m_rRAS.good()) mbStatus = false; } rBitmap.SetPixel(y, x, SanitizePaletteIndex(rvPalette, sal::static_int_cast< sal_uInt8 >( nDat >> ( ( x & 7 ) ^ 7 )))); } if (!( ( x - 1 ) & 0x8 ) ) { ImplGetByte(); // WORD ALIGNMENT ??? if (!m_rRAS.good()) mbStatus = false; } } break; } case 8 : for (y = 0; y < mnHeight && mbStatus; ++y) { for (x = 0; x < mnWidth && mbStatus; ++x) { sal_uInt8 nDat = ImplGetByte(); rBitmap.SetPixel(y, x, SanitizePaletteIndex(rvPalette, nDat)); if (!m_rRAS.good()) mbStatus = false; } if ( x & 1 ) { ImplGetByte(); // WORD ALIGNMENT ??? if (!m_rRAS.good()) mbStatus = false; } } break; case 24 : switch ( mnDepth ) { case 24 : for (y = 0; y < mnHeight && mbStatus; ++y) { for (x = 0; x < mnWidth && mbStatus; ++x) { if ( mnType == RAS_TYPE_RGB_FORMAT ) { nRed = ImplGetByte(); nGreen = ImplGetByte(); nBlue = ImplGetByte(); } else { nBlue = ImplGetByte(); nGreen = ImplGetByte(); nRed = ImplGetByte(); } rBitmap.SetPixel(y, x, Color(nRed, nGreen, nBlue)); if (!m_rRAS.good()) mbStatus = false; } if ( x & 1 ) { ImplGetByte(); // WORD ALIGNMENT ??? if (!m_rRAS.good()) mbStatus = false; } } break; case 32 : for (y = 0; y < mnHeight && mbStatus; ++y) { for (x = 0; x < mnWidth && mbStatus; ++x) { ImplGetByte(); // pad byte > nil if ( mnType == RAS_TYPE_RGB_FORMAT ) { nRed = ImplGetByte(); nGreen = ImplGetByte(); nBlue = ImplGetByte(); } else { nBlue = ImplGetByte(); nGreen = ImplGetByte(); nRed = ImplGetByte(); } rBitmap.SetPixel(y, x, Color(nRed, nGreen, nBlue)); if (!m_rRAS.good()) mbStatus = false; } } break; } break; default: mbStatus = false; break; } return mbStatus; } sal_uInt8 RASReader::ImplGetByte() { sal_uInt8 nRetVal(0); if ( mnType != RAS_TYPE_BYTE_ENCODED ) { m_rRAS.ReadUChar( nRetVal ); return nRetVal; } else { if ( mnRepCount ) { mnRepCount--; return mnRepVal; } else { m_rRAS.ReadUChar( nRetVal ); if ( nRetVal != 0x80 ) return nRetVal; m_rRAS.ReadUChar( nRetVal ); if ( nRetVal == 0 ) return 0x80; mnRepCount = nRetVal ; m_rRAS.ReadUChar( mnRepVal ); return mnRepVal; } } } //================== GraphicImport - the exported function ================ bool ImportRasGraphic( SvStream & rStream, Graphic & rGraphic) { bool bRet = false; try { RASReader aRASReader(rStream); bRet = aRASReader.ReadRAS(rGraphic ); } catch (...) { } return bRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */