mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-24 15:16:38 +00:00
Migrate image storage from PICT to BMP
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "PLLittleEndian.h"
|
||||
#include "PLLittleEndian.h"
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
@@ -11,14 +11,14 @@ namespace PortabilityLayer
|
||||
}
|
||||
|
||||
struct BitmapFileHeader
|
||||
{
|
||||
{
|
||||
char m_id[2]; // Normally "BM"
|
||||
LEUInt32_t m_fileSize;
|
||||
LEUInt16_t m_reserved1;
|
||||
LEUInt16_t m_reserved2;
|
||||
LEUInt32_t m_imageDataStart;
|
||||
LEUInt32_t m_imageDataStart;
|
||||
};
|
||||
|
||||
|
||||
struct BitmapInfoHeader
|
||||
{
|
||||
LEUInt32_t m_thisStructureSize;
|
||||
|
12
PortabilityLayer/BitmapImage.cpp
Normal file
12
PortabilityLayer/BitmapImage.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "BMPFormat.h"
|
||||
#include "BitmapImage.h"
|
||||
|
||||
Rect BitmapImage::GetRect() const
|
||||
{
|
||||
const PortabilityLayer::BitmapInfoHeader *infoHeader = reinterpret_cast<const PortabilityLayer::BitmapInfoHeader*>(reinterpret_cast<const uint8_t*>(this) + sizeof(PortabilityLayer::BitmapFileHeader));
|
||||
|
||||
const uint32_t width = infoHeader->m_width;
|
||||
const uint32_t height = infoHeader->m_height;
|
||||
|
||||
return Rect::Create(0, 0, static_cast<int16_t>(height), static_cast<int16_t>(width));
|
||||
}
|
11
PortabilityLayer/BitmapImage.h
Normal file
11
PortabilityLayer/BitmapImage.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "BMPFormat.h"
|
||||
#include "SharedTypes.h"
|
||||
|
||||
struct BitmapImage
|
||||
{
|
||||
PortabilityLayer::BitmapFileHeader m_fileHeader;
|
||||
|
||||
Rect GetRect() const;
|
||||
};
|
@@ -15,7 +15,7 @@ namespace PortabilityLayer
|
||||
|
||||
bool ImageWidget::Init(const WidgetBasicState &state)
|
||||
{
|
||||
m_pict = PortabilityLayer::ResourceManager::GetInstance()->GetResource('PICT', state.m_resID).StaticCast<Picture>();
|
||||
m_pict = PortabilityLayer::ResourceManager::GetInstance()->GetResource('PICT', state.m_resID).StaticCast<BitmapImage>();
|
||||
|
||||
if (!m_pict)
|
||||
return false;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
#include "PLWidgets.h"
|
||||
#include "PLHandle.h"
|
||||
|
||||
struct Picture;
|
||||
struct BitmapImage;
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
@@ -17,6 +17,6 @@ namespace PortabilityLayer
|
||||
void DrawControl(DrawSurface *surface) override;
|
||||
|
||||
private:
|
||||
THandle<Picture> m_pict;
|
||||
THandle<BitmapImage> m_pict;
|
||||
};
|
||||
}
|
||||
|
@@ -36,9 +36,9 @@ PixMapHandle GetGWorldPixMap(DrawSurface *gworld)
|
||||
return gworld->m_port.GetPixMap();
|
||||
}
|
||||
|
||||
THandle<Picture> GetPicture(short resID)
|
||||
THandle<BitmapImage> GetPicture(short resID)
|
||||
{
|
||||
return PortabilityLayer::ResourceManager::GetInstance()->GetResource('PICT', resID).StaticCast<Picture>();
|
||||
return PortabilityLayer::ResourceManager::GetInstance()->GetResource('PICT', resID).StaticCast<BitmapImage>();
|
||||
}
|
||||
|
||||
void OffsetRect(Rect *rect, int right, int down)
|
||||
|
@@ -22,7 +22,7 @@ void DisposeGWorld(DrawSurface *gworld);
|
||||
|
||||
PixMapHandle GetGWorldPixMap(DrawSurface *gworld);
|
||||
|
||||
THandle<Picture> GetPicture(short resID);
|
||||
THandle<BitmapImage> GetPicture(short resID);
|
||||
|
||||
void OffsetRect(Rect *rect, int right, int down);
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include "PLQDraw.h"
|
||||
#include "QDManager.h"
|
||||
#include "QDState.h"
|
||||
#include "BitmapImage.h"
|
||||
#include "DisplayDeviceManager.h"
|
||||
#include "FontFamily.h"
|
||||
#include "FontManager.h"
|
||||
@@ -22,11 +23,8 @@
|
||||
#include "ScanlineMaskIterator.h"
|
||||
#include "QDGraf.h"
|
||||
#include "QDStandardPalette.h"
|
||||
#include "QDPictEmitContext.h"
|
||||
#include "QDPictEmitScanlineParameters.h"
|
||||
#include "WindowManager.h"
|
||||
#include "QDGraf.h"
|
||||
#include "QDPictDecoder.h"
|
||||
#include "QDPixMap.h"
|
||||
#include "Vec2i.h"
|
||||
|
||||
@@ -34,220 +32,6 @@
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
class PixMapBlitEmitter final : public QDPictEmitContext
|
||||
{
|
||||
public:
|
||||
PixMapBlitEmitter(const Vec2i &drawOrigin, PixMapImpl *pixMap);
|
||||
~PixMapBlitEmitter();
|
||||
|
||||
bool SpecifyFrame(const Rect &rect) override;
|
||||
Rect ConstrainRegion(const Rect &rect) const override;
|
||||
void Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms) override;
|
||||
void BlitScanlineAndAdvance(const void *) override;
|
||||
bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) override;
|
||||
|
||||
private:
|
||||
PixMapImpl *m_pixMap;
|
||||
Vec2i m_drawOrigin;
|
||||
uint8_t *m_tempBuffer;
|
||||
Rect m_specFrame;
|
||||
|
||||
QDPictBlitSourceType m_blitType;
|
||||
QDPictEmitScanlineParameters m_params;
|
||||
|
||||
size_t m_constraintRegionWidth;
|
||||
size_t m_constraintRegionStartIndex;
|
||||
size_t m_constraintRegionEndIndex;
|
||||
size_t m_outputIndexStart;
|
||||
|
||||
uint8_t m_paletteMap[256];
|
||||
bool m_sourceAndDestPalettesAreSame;
|
||||
};
|
||||
|
||||
PixMapBlitEmitter::PixMapBlitEmitter(const Vec2i &drawOrigin, PixMapImpl *pixMap)
|
||||
: m_pixMap(pixMap)
|
||||
, m_drawOrigin(drawOrigin)
|
||||
, m_tempBuffer(nullptr)
|
||||
, m_sourceAndDestPalettesAreSame(false)
|
||||
{
|
||||
}
|
||||
|
||||
PixMapBlitEmitter::~PixMapBlitEmitter()
|
||||
{
|
||||
if (m_tempBuffer)
|
||||
PortabilityLayer::MemoryManager::GetInstance()->Release(m_tempBuffer);
|
||||
}
|
||||
|
||||
bool PixMapBlitEmitter::SpecifyFrame(const Rect &rect)
|
||||
{
|
||||
m_specFrame = rect;
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect PixMapBlitEmitter::ConstrainRegion(const Rect &rect) const
|
||||
{
|
||||
const Rect pixMapRect = m_pixMap->m_rect;
|
||||
|
||||
const Rect2i rectInDrawSpace = Rect2i(rect) + m_drawOrigin;
|
||||
|
||||
const Rect2i constrainedRectInDrawSpace = rectInDrawSpace.Intersect(Rect2i(pixMapRect));
|
||||
|
||||
// If this got completely culled away, return an empty rect, but avoid int truncation
|
||||
if (!constrainedRectInDrawSpace.IsValid())
|
||||
return Rect::Create(rect.top, rect.left, rect.top, rect.left);
|
||||
|
||||
// Otherwise, it should still be valid in the picture space
|
||||
const Rect2i constrainedRectInPictSpace = constrainedRectInDrawSpace - m_drawOrigin;
|
||||
return constrainedRectInPictSpace.ToShortRect();
|
||||
}
|
||||
|
||||
void PixMapBlitEmitter::Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms)
|
||||
{
|
||||
// FIXME: Detect different system palette (if we ever do that)
|
||||
if (QDPictBlitSourceType_IsIndexed(sourceType))
|
||||
{
|
||||
if (params.m_numColors == 256 && !memcmp(params.m_colors, StandardPalette::GetInstance()->GetColors(), sizeof(RGBAColor) * 256))
|
||||
m_sourceAndDestPalettesAreSame = true;
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
m_blitType = sourceType;
|
||||
m_params = params;
|
||||
|
||||
m_constraintRegionWidth = params.m_constrainedRegionRight - params.m_constrainedRegionLeft;
|
||||
m_constraintRegionStartIndex = params.m_constrainedRegionLeft - params.m_scanlineOriginX;
|
||||
m_constraintRegionEndIndex = params.m_constrainedRegionRight - params.m_scanlineOriginX;
|
||||
|
||||
const size_t firstCol = params.m_constrainedRegionLeft + m_drawOrigin.m_x - m_pixMap->m_rect.left;
|
||||
const size_t firstRow = params.m_firstY + m_drawOrigin.m_y - m_pixMap->m_rect.top;
|
||||
|
||||
m_outputIndexStart = firstRow * m_pixMap->GetPitch() + firstCol;
|
||||
}
|
||||
|
||||
void PixMapBlitEmitter::BlitScanlineAndAdvance(const void *data)
|
||||
{
|
||||
const int32_t crRight = m_params.m_constrainedRegionRight;
|
||||
const int32_t crLeft = m_params.m_constrainedRegionLeft;
|
||||
const size_t constraintRegionStartIndex = m_constraintRegionStartIndex;
|
||||
const uint8_t *dataBytes = static_cast<const uint8_t*>(data);
|
||||
const size_t outputIndexStart = m_outputIndexStart;
|
||||
const size_t planarSeparation = m_params.m_planarSeparation;
|
||||
const size_t constraintRegionWidth = m_constraintRegionWidth;
|
||||
|
||||
const uint8_t *paletteMapping = nullptr;
|
||||
|
||||
const uint8_t staticMapping1Bit[] = { 0, 255 };
|
||||
|
||||
void *imageData = m_pixMap->GetPixelData();
|
||||
|
||||
if (m_pixMap->GetPixelFormat() == GpPixelFormats::k8BitStandard || m_pixMap->GetPixelFormat() == GpPixelFormats::kBW1)
|
||||
{
|
||||
switch (m_blitType)
|
||||
{
|
||||
case QDPictBlitSourceType_Indexed1Bit:
|
||||
for (size_t i = 0; i < constraintRegionWidth; i++)
|
||||
{
|
||||
const size_t itemIndex = i + constraintRegionStartIndex;
|
||||
|
||||
const int bitShift = 7 - (itemIndex & 7);
|
||||
const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1;
|
||||
static_cast<uint8_t*>(imageData)[i + outputIndexStart] = paletteMapping[colorIndex];
|
||||
}
|
||||
break;
|
||||
case QDPictBlitSourceType_Indexed2Bit:
|
||||
for (size_t i = 0; i < constraintRegionWidth; i++)
|
||||
{
|
||||
const size_t itemIndex = i + constraintRegionStartIndex;
|
||||
|
||||
const int bitShift = 6 - (2 * (itemIndex & 1));
|
||||
const int colorIndex = (dataBytes[itemIndex / 4] >> bitShift) & 0x3;
|
||||
static_cast<uint8_t*>(imageData)[i + outputIndexStart] = paletteMapping[colorIndex];
|
||||
}
|
||||
break;
|
||||
case QDPictBlitSourceType_Indexed4Bit:
|
||||
for (size_t i = 0; i < constraintRegionWidth; i++)
|
||||
{
|
||||
const size_t itemIndex = i + constraintRegionStartIndex;
|
||||
|
||||
const int bitShift = 4 - (4 * (itemIndex & 1));
|
||||
const int colorIndex = (dataBytes[itemIndex / 2] >> bitShift) & 0xf;
|
||||
static_cast<uint8_t*>(imageData)[i + outputIndexStart] = paletteMapping[colorIndex];
|
||||
}
|
||||
break;
|
||||
case QDPictBlitSourceType_Indexed8Bit:
|
||||
if (m_sourceAndDestPalettesAreSame)
|
||||
memcpy(static_cast<uint8_t*>(imageData) + outputIndexStart, dataBytes + constraintRegionStartIndex, m_constraintRegionWidth);
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < constraintRegionWidth; i++)
|
||||
{
|
||||
const size_t itemIndex = i + constraintRegionStartIndex;
|
||||
const uint8_t colorIndex = dataBytes[itemIndex];
|
||||
static_cast<uint8_t*>(imageData)[i + outputIndexStart] = paletteMapping[colorIndex];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QDPictBlitSourceType_1Bit:
|
||||
for (size_t i = 0; i < constraintRegionWidth; i++)
|
||||
{
|
||||
const size_t itemIndex = i + constraintRegionStartIndex;
|
||||
|
||||
const int bitShift = 7 - (itemIndex & 7);
|
||||
const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1;
|
||||
static_cast<uint8_t*>(imageData)[i + outputIndexStart] = staticMapping1Bit[colorIndex];
|
||||
}
|
||||
break;
|
||||
case QDPictBlitSourceType_RGB15:
|
||||
for (size_t i = 0; i < constraintRegionWidth; i++)
|
||||
{
|
||||
const size_t itemIndex = i + constraintRegionStartIndex;
|
||||
|
||||
const uint16_t item = *reinterpret_cast<const uint16_t*>(dataBytes + itemIndex * 2);
|
||||
uint8_t &outputItem = static_cast<uint8_t*>(imageData)[i + outputIndexStart];
|
||||
|
||||
outputItem = StandardPalette::GetInstance()->MapColorLUT((item >> 1) & 0xf, (item >> 6) & 0xf, (item >> 11) & 0x1f);
|
||||
}
|
||||
break;
|
||||
case QDPictBlitSourceType_RGB24_Multiplane:
|
||||
for (size_t i = 0; i < m_constraintRegionWidth; i++)
|
||||
{
|
||||
const size_t itemIndex = i + constraintRegionStartIndex;
|
||||
|
||||
uint8_t &outputItem = static_cast<uint8_t*>(imageData)[i + outputIndexStart];
|
||||
|
||||
const uint8_t r = dataBytes[itemIndex];
|
||||
const uint8_t g = dataBytes[itemIndex + planarSeparation];
|
||||
const uint8_t b = dataBytes[itemIndex + planarSeparation * 2];
|
||||
|
||||
outputItem = StandardPalette::GetInstance()->MapColorLUT(r, g, b);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
m_outputIndexStart += m_pixMap->GetPitch();
|
||||
}
|
||||
|
||||
bool PixMapBlitEmitter::AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size)
|
||||
{
|
||||
m_tempBuffer = static_cast<uint8_t*>(PortabilityLayer::MemoryManager::GetInstance()->Alloc(buffer1Size + buffer2Size));
|
||||
if (!m_tempBuffer)
|
||||
return false;
|
||||
|
||||
buffer1 = m_tempBuffer;
|
||||
buffer2 = m_tempBuffer + buffer1Size;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void GetPort(GrafPtr *graf)
|
||||
{
|
||||
PL_NotYetImplemented();
|
||||
@@ -820,7 +604,7 @@ int32_t DrawSurface::MeasureFontLineGap()
|
||||
return rfont->GetMetrics().m_linegap;
|
||||
}
|
||||
|
||||
void DrawSurface::DrawPicture(THandle<Picture> pictHdl, const Rect &bounds)
|
||||
void DrawSurface::DrawPicture(THandle<BitmapImage> pictHdl, const Rect &bounds)
|
||||
{
|
||||
if (!pictHdl)
|
||||
return;
|
||||
@@ -828,11 +612,15 @@ void DrawSurface::DrawPicture(THandle<Picture> pictHdl, const Rect &bounds)
|
||||
if (!bounds.IsValid() || bounds.Width() == 0 || bounds.Height() == 0)
|
||||
return;
|
||||
|
||||
Picture *picPtr = *pictHdl;
|
||||
if (!picPtr)
|
||||
BitmapImage *bmpPtr = *pictHdl;
|
||||
if (!bmpPtr)
|
||||
return;
|
||||
|
||||
const Rect picRect = picPtr->picFrame.ToRect();
|
||||
const size_t bmpSize = bmpPtr->m_fileHeader.m_fileSize;
|
||||
const Rect picRect = bmpPtr->GetRect();
|
||||
|
||||
if (picRect.Width() == 0 || picRect.Height() == 0)
|
||||
return;
|
||||
|
||||
if (bounds.right - bounds.left != picRect.right - picRect.left || bounds.bottom - bounds.top != picRect.bottom - picRect.top)
|
||||
{
|
||||
@@ -868,20 +656,245 @@ void DrawSurface::DrawPicture(THandle<Picture> pictHdl, const Rect &bounds)
|
||||
PortabilityLayer::PixMapImpl *pixMap = static_cast<PortabilityLayer::PixMapImpl*>(*port->GetPixMap());
|
||||
|
||||
long handleSize = pictHdl.MMBlock()->m_size;
|
||||
PortabilityLayer::MemReaderStream stream(picPtr, handleSize);
|
||||
PortabilityLayer::MemReaderStream stream(bmpPtr, handleSize);
|
||||
|
||||
// Adjust draw origin
|
||||
const PortabilityLayer::Vec2i drawOrigin = PortabilityLayer::Vec2i(bounds.left - picPtr->picFrame.left, bounds.top - picPtr->picFrame.top);
|
||||
const PortabilityLayer::Vec2i drawOrigin = PortabilityLayer::Vec2i(bounds.left, bounds.top);
|
||||
const Rect targetPixMapRect = pixMap->m_rect;
|
||||
|
||||
switch (pixMap->GetPixelFormat())
|
||||
const int32_t truncatedTop = std::max<int32_t>(0, targetPixMapRect.top - bounds.top);
|
||||
const int32_t truncatedBottom = std::max<int32_t>(0, bounds.bottom - targetPixMapRect.bottom);
|
||||
const int32_t truncatedLeft = std::max<int32_t>(0, targetPixMapRect.left - bounds.left);
|
||||
const int32_t truncatedRight = std::max<int32_t>(0, bounds.right - targetPixMapRect.right);
|
||||
|
||||
uint8_t paletteMapping[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
paletteMapping[i] = 0;
|
||||
|
||||
// Parse bitmap header
|
||||
const uint8_t *bmpBytes = reinterpret_cast<const uint8_t*>(bmpPtr);
|
||||
|
||||
PortabilityLayer::BitmapFileHeader fileHeader;
|
||||
PortabilityLayer::BitmapInfoHeader infoHeader;
|
||||
|
||||
memcpy(&fileHeader, bmpBytes, sizeof(fileHeader));
|
||||
memcpy(&infoHeader, bmpBytes + sizeof(fileHeader), sizeof(infoHeader));
|
||||
|
||||
const uint16_t bpp = infoHeader.m_bitsPerPixel;
|
||||
|
||||
if (bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24)
|
||||
return;
|
||||
|
||||
const uint32_t numColors = infoHeader.m_numColors;
|
||||
if (numColors > 256)
|
||||
return;
|
||||
|
||||
const uint8_t *ctabLoc = bmpBytes + sizeof(fileHeader) + infoHeader.m_thisStructureSize;
|
||||
|
||||
const size_t ctabSize = infoHeader.m_numColors * sizeof(PortabilityLayer::BitmapColorTableEntry);
|
||||
const size_t availCTabBytes = bmpSize - sizeof(fileHeader) - infoHeader.m_thisStructureSize;
|
||||
|
||||
if (ctabSize > availCTabBytes)
|
||||
return;
|
||||
|
||||
if (bpp <= 8)
|
||||
{
|
||||
// Perform palette mapping
|
||||
if (pixMap->GetPixelFormat() == GpPixelFormats::kBW1)
|
||||
{
|
||||
const PortabilityLayer::BitmapColorTableEntry *ctab = reinterpret_cast<const PortabilityLayer::BitmapColorTableEntry*>(ctabLoc);
|
||||
for (size_t i = 0; i < numColors; i++)
|
||||
{
|
||||
const PortabilityLayer::BitmapColorTableEntry &ctabEntry = ctab[i];
|
||||
if (ctabEntry.m_r + ctabEntry.m_g + ctabEntry.m_b < 383)
|
||||
paletteMapping[i] = 255;
|
||||
else
|
||||
paletteMapping[i] = 0;
|
||||
}
|
||||
}
|
||||
else if (pixMap->GetPixelFormat() == GpPixelFormats::k8BitStandard)
|
||||
{
|
||||
const PortabilityLayer::BitmapColorTableEntry *ctab = reinterpret_cast<const PortabilityLayer::BitmapColorTableEntry*>(ctabLoc);
|
||||
for (size_t i = 0; i < numColors; i++)
|
||||
{
|
||||
const PortabilityLayer::BitmapColorTableEntry &ctabEntry = ctab[i];
|
||||
paletteMapping[i] = PortabilityLayer::StandardPalette::GetInstance()->MapColorLUT(PortabilityLayer::RGBAColor::Create(ctabEntry.m_r, ctabEntry.m_g, ctabEntry.m_b, 255));
|
||||
const PortabilityLayer::RGBAColor &resultColor = PortabilityLayer::StandardPalette::GetInstance()->GetColors()[paletteMapping[i]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PL_NotYetImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t imageDataOffset = fileHeader.m_imageDataStart;
|
||||
|
||||
if (imageDataOffset > bmpSize)
|
||||
return;
|
||||
|
||||
const size_t availImageDataSize = bmpSize - imageDataOffset;
|
||||
|
||||
const size_t sourcePitch = (bpp * infoHeader.m_width + 31) / 32 * 4;
|
||||
const size_t inDataSize = sourcePitch * infoHeader.m_height;
|
||||
|
||||
if (inDataSize > availImageDataSize)
|
||||
return;
|
||||
|
||||
const uint8_t *imageDataStart = reinterpret_cast<const uint8_t*>(bmpPtr) + imageDataOffset;
|
||||
const uint8_t *sourceFirstImageRowStart = imageDataStart + (infoHeader.m_height - 1) * sourcePitch;
|
||||
|
||||
// Determine rect
|
||||
const PortabilityLayer::Rect2i sourceRect = PortabilityLayer::Rect2i(truncatedTop, truncatedLeft, static_cast<int32_t>(infoHeader.m_height) - truncatedBottom, static_cast<int32_t>(infoHeader.m_width) - truncatedRight);
|
||||
|
||||
if (sourceRect.m_topLeft.m_x >= sourceRect.m_bottomRight.m_x || sourceRect.m_topLeft.m_y >= sourceRect.m_bottomRight.m_y)
|
||||
return; // Entire rect was culled away
|
||||
|
||||
const uint32_t numCopyRows = static_cast<uint32_t>(sourceRect.m_bottomRight.m_y - sourceRect.m_topLeft.m_y);
|
||||
const uint32_t numCopyCols = static_cast<uint32_t>(sourceRect.m_bottomRight.m_x - sourceRect.m_topLeft.m_x);
|
||||
|
||||
const uint8_t *firstSourceRow = sourceFirstImageRowStart - static_cast<uint32_t>(sourceRect.m_topLeft.m_y) * sourcePitch;
|
||||
int32_t firstSourceCol = sourceRect.m_topLeft.m_x;
|
||||
|
||||
const size_t destPitch = pixMap->GetPitch();
|
||||
uint8_t *firstDestRow = static_cast<uint8_t*>(pixMap->GetPixelData()) + destPitch * static_cast<uint32_t>(drawOrigin.m_y + truncatedTop);
|
||||
size_t firstDestCol = static_cast<uint32_t>(drawOrigin.m_x + truncatedLeft);
|
||||
|
||||
const PortabilityLayer::StandardPalette *stdPalette = PortabilityLayer::StandardPalette::GetInstance();
|
||||
|
||||
GpPixelFormat_t destFormat = pixMap->GetPixelFormat();
|
||||
switch (destFormat)
|
||||
{
|
||||
case GpPixelFormats::kBW1:
|
||||
case GpPixelFormats::k8BitStandard:
|
||||
{
|
||||
PortabilityLayer::PixMapBlitEmitter blitEmitter(drawOrigin, pixMap);
|
||||
PortabilityLayer::QDPictDecoder decoder;
|
||||
const uint8_t *currentSourceRow = firstSourceRow;
|
||||
uint8_t *currentDestRow = firstDestRow;
|
||||
for (uint32_t row = 0; row < numCopyRows; row++)
|
||||
{
|
||||
assert(currentSourceRow >= imageDataStart && currentSourceRow <= imageDataStart + inDataSize);
|
||||
|
||||
decoder.DecodePict(&stream, &blitEmitter);
|
||||
if (bpp == 1)
|
||||
{
|
||||
for (size_t col = 0; col < numCopyCols; col++)
|
||||
{
|
||||
const size_t srcColIndex = col + firstSourceCol;
|
||||
const size_t destColIndex = col + firstDestCol;
|
||||
|
||||
const unsigned int srcIndex = (currentSourceRow[srcColIndex / 8] >> (8 - ((srcColIndex & 7) + 1))) & 0x01;
|
||||
currentDestRow[destColIndex] = paletteMapping[srcIndex];
|
||||
}
|
||||
}
|
||||
else if (bpp == 4)
|
||||
{
|
||||
for (size_t col = 0; col < numCopyCols; col++)
|
||||
{
|
||||
const size_t srcColIndex = col + firstSourceCol;
|
||||
const size_t destColIndex = col + firstDestCol;
|
||||
|
||||
const unsigned int srcIndex = (currentSourceRow[srcColIndex / 2] >> (8 - ((srcColIndex & 1) + 1) * 4)) & 0x0f;
|
||||
currentDestRow[destColIndex] = paletteMapping[srcIndex];
|
||||
}
|
||||
}
|
||||
else if (bpp == 8)
|
||||
{
|
||||
for (size_t col = 0; col < numCopyCols; col++)
|
||||
{
|
||||
const size_t srcColIndex = col + firstSourceCol;
|
||||
const size_t destColIndex = col + firstDestCol;
|
||||
|
||||
const unsigned int srcIndex = currentSourceRow[srcColIndex];
|
||||
currentDestRow[destColIndex] = paletteMapping[srcIndex];
|
||||
}
|
||||
}
|
||||
else if (bpp == 16)
|
||||
{
|
||||
if (destFormat == GpPixelFormats::kBW1)
|
||||
{
|
||||
for (size_t col = 0; col < numCopyCols; col++)
|
||||
{
|
||||
const size_t srcColIndex = col + firstSourceCol;
|
||||
const size_t destColIndex = col + firstDestCol;
|
||||
|
||||
const uint8_t srcLow = currentSourceRow[srcColIndex * 2 + 0];
|
||||
const uint8_t srcHigh = currentSourceRow[srcColIndex * 2 + 1];
|
||||
|
||||
const unsigned int combinedValue = srcLow | (srcHigh << 8);
|
||||
const unsigned int b = (srcLow & 0x1f);
|
||||
const unsigned int g = ((srcLow >> 5) & 0x1f);
|
||||
const unsigned int r = ((srcLow >> 10) & 0x1f);
|
||||
|
||||
if (r + g + b > 46)
|
||||
currentDestRow[destColIndex] = 0;
|
||||
else
|
||||
currentDestRow[destColIndex] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t col = 0; col < numCopyCols; col++)
|
||||
{
|
||||
const size_t srcColIndex = col + firstSourceCol;
|
||||
const size_t destColIndex = col + firstDestCol;
|
||||
|
||||
const uint8_t srcLow = currentSourceRow[srcColIndex * 2 + 0];
|
||||
const uint8_t srcHigh = currentSourceRow[srcColIndex * 2 + 1];
|
||||
|
||||
const unsigned int combinedValue = srcLow | (srcHigh << 8);
|
||||
const unsigned int b = (srcLow & 0x1f);
|
||||
const unsigned int g = ((srcLow >> 5) & 0x1f);
|
||||
const unsigned int r = ((srcLow >> 10) & 0x1f);
|
||||
|
||||
const unsigned int xr = (r << 5) | (r >> 2);
|
||||
const unsigned int xg = (g << 5) | (g >> 2);
|
||||
const unsigned int xb = (b << 5) | (b >> 2);
|
||||
|
||||
currentDestRow[destColIndex] = stdPalette->MapColorLUT(PortabilityLayer::RGBAColor::Create(xr, xg, xb, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bpp == 24)
|
||||
{
|
||||
if (destFormat == GpPixelFormats::kBW1)
|
||||
{
|
||||
for (size_t col = 0; col < numCopyCols; col++)
|
||||
{
|
||||
const size_t srcColIndex = col + firstSourceCol;
|
||||
const size_t destColIndex = col + firstDestCol;
|
||||
|
||||
const uint8_t srcLow = currentSourceRow[srcColIndex * 2 + 0];
|
||||
const uint8_t srcHigh = currentSourceRow[srcColIndex * 2 + 1];
|
||||
|
||||
const unsigned int combinedValue = srcLow | (srcHigh << 8);
|
||||
const unsigned int b = (srcLow & 0x1f);
|
||||
const unsigned int g = ((srcLow >> 5) & 0x1f);
|
||||
const unsigned int r = ((srcLow >> 10) & 0x1f);
|
||||
|
||||
if (r + g + b > 46)
|
||||
currentDestRow[destColIndex] = 0;
|
||||
else
|
||||
currentDestRow[destColIndex] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t col = 0; col < numCopyCols; col++)
|
||||
{
|
||||
const size_t srcColIndex = col + firstSourceCol;
|
||||
const size_t destColIndex = col + firstDestCol;
|
||||
|
||||
const unsigned int b = currentSourceRow[srcColIndex * 3 + 0];
|
||||
const unsigned int g = currentSourceRow[srcColIndex * 3 + 1];
|
||||
const unsigned int r = currentSourceRow[srcColIndex * 3 + 2];
|
||||
|
||||
currentDestRow[destColIndex] = stdPalette->MapColorLUT(PortabilityLayer::RGBAColor::Create(r, g, b, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentSourceRow -= sourcePitch;
|
||||
currentDestRow += destPitch;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include "VirtualDirectory.h"
|
||||
#include "WaveFormat.h"
|
||||
#include "ZipFileProxy.h"
|
||||
#include "ZipFile.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -35,8 +36,8 @@ typedef ResourceValidationRules::ResourceValidationRule ResourceValidationRule_t
|
||||
|
||||
namespace
|
||||
{
|
||||
// Validation is only intended to validate enough of the file structure that constrained validation can be performed
|
||||
// without needing to pass the file size
|
||||
// Validation here is only intended to be minimal, to ensure later checks can determine the format size and do certain operations
|
||||
// that must be valid.
|
||||
static bool ValidateResource(const void *res, size_t size, ResourceValidationRule_t validationRule)
|
||||
{
|
||||
switch (validationRule)
|
||||
@@ -57,7 +58,7 @@ namespace
|
||||
|
||||
case ResourceValidationRules::kBMP:
|
||||
{
|
||||
if (size < sizeof(PortabilityLayer::BitmapFileHeader))
|
||||
if (size < sizeof(PortabilityLayer::BitmapFileHeader) + sizeof(PortabilityLayer::BitmapInfoHeader))
|
||||
return false;
|
||||
|
||||
PortabilityLayer::BitmapFileHeader mainHeader;
|
||||
@@ -65,6 +66,19 @@ namespace
|
||||
if (mainHeader.m_fileSize > size)
|
||||
return false;
|
||||
|
||||
PortabilityLayer::BitmapInfoHeader infoHeader;
|
||||
memcpy(&infoHeader, static_cast<const uint8_t*>(res) + sizeof(mainHeader), sizeof(infoHeader));
|
||||
if (infoHeader.m_thisStructureSize < sizeof(PortabilityLayer::BitmapInfoHeader))
|
||||
return false;
|
||||
|
||||
const size_t sizeForInfoHeader = size - sizeof(PortabilityLayer::BitmapFileHeader);
|
||||
if (infoHeader.m_thisStructureSize > sizeForInfoHeader)
|
||||
return false;
|
||||
|
||||
// Dimensions need to fit in 16-bit signed space
|
||||
if (infoHeader.m_width >= 0x8000 || infoHeader.m_height >= 0x8000)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@@ -72,6 +86,8 @@ namespace
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,17 +305,22 @@ namespace PortabilityLayer
|
||||
|
||||
PLError_t ResourceManagerImpl::CreateBlankResFile(VirtualDirectory_t virtualDir, const PLPasStr &filename)
|
||||
{
|
||||
const uint8_t blankResFileData[] = {
|
||||
0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 30,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 30, 255, 255 };
|
||||
|
||||
PortabilityLayer::IOStream *stream = nullptr;
|
||||
PLError_t error = FileManager::GetInstance()->RawOpenFileResources(virtualDir, filename, EFilePermission_Write, true, GpFileCreationDispositions::kCreateOrOverwrite, stream);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (stream->Write(blankResFileData, sizeof(blankResFileData)) != sizeof(blankResFileData))
|
||||
PortabilityLayer::ZipEndOfCentralDirectoryRecord eocd;
|
||||
eocd.m_signature = PortabilityLayer::ZipEndOfCentralDirectoryRecord::kSignature;
|
||||
eocd.m_thisDiskNumber = 0;
|
||||
eocd.m_centralDirDisk = 0;
|
||||
eocd.m_numCentralDirRecordsThisDisk = 0;
|
||||
eocd.m_numCentralDirRecords = 0;
|
||||
eocd.m_centralDirectorySizeBytes = 0;
|
||||
eocd.m_centralDirStartOffset = 0;
|
||||
eocd.m_commentLength = 0;
|
||||
|
||||
if (stream->Write(&eocd, sizeof(eocd)) != sizeof(eocd))
|
||||
{
|
||||
stream->Close();
|
||||
return PLErrors::kIOError;
|
||||
@@ -347,12 +368,16 @@ namespace PortabilityLayer
|
||||
|
||||
size_t numFiles = zipFileProxy->NumFiles();
|
||||
|
||||
ResourceArchiveRef *refs = static_cast<ResourceArchiveRef*>(mm->Alloc(sizeof(ResourceArchiveRef) * numFiles));
|
||||
if (!refs)
|
||||
return nullptr;
|
||||
ResourceArchiveRef *refs = nullptr;
|
||||
if (numFiles > 0)
|
||||
{
|
||||
refs = static_cast<ResourceArchiveRef*>(mm->Alloc(sizeof(ResourceArchiveRef) * numFiles));
|
||||
if (!refs)
|
||||
return nullptr;
|
||||
|
||||
for (size_t i = 0; i < numFiles; i++)
|
||||
new (refs + i) ResourceArchiveRef();
|
||||
for (size_t i = 0; i < numFiles; i++)
|
||||
new (refs + i) ResourceArchiveRef();
|
||||
}
|
||||
|
||||
void *storage = mm->Alloc(sizeof(ResourceArchive));
|
||||
if (!storage)
|
||||
@@ -380,6 +405,11 @@ namespace PortabilityLayer
|
||||
extension = ".wav";
|
||||
validationRule = ResourceValidationRules::kWAV;
|
||||
}
|
||||
else if (resTypeID == ResTypeID('Date') || resTypeID == ResTypeID('PICT'))
|
||||
{
|
||||
extension = ".bmp";
|
||||
validationRule = ResourceValidationRules::kBMP;
|
||||
}
|
||||
|
||||
char resourceFile[64];
|
||||
|
||||
|
@@ -141,6 +141,7 @@
|
||||
<ClInclude Include="AntiAliasTable.h" />
|
||||
<ClInclude Include="BinarySearch.h" />
|
||||
<ClInclude Include="BinHex4.h" />
|
||||
<ClInclude Include="BitmapImage.h" />
|
||||
<ClInclude Include="BMPFormat.h" />
|
||||
<ClInclude Include="BytePack.h" />
|
||||
<ClInclude Include="ByteSwap.h" />
|
||||
@@ -292,6 +293,7 @@
|
||||
<ClCompile Include="AEManager.cpp" />
|
||||
<ClCompile Include="AntiAliasTable.cpp" />
|
||||
<ClCompile Include="BinHex4.cpp" />
|
||||
<ClCompile Include="BitmapImage.cpp" />
|
||||
<ClCompile Include="ByteSwap.cpp" />
|
||||
<ClCompile Include="CFileStream.cpp" />
|
||||
<ClCompile Include="DeflateCodec.cpp" />
|
||||
|
@@ -468,6 +468,9 @@
|
||||
<ClInclude Include="AntiAliasTable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BitmapImage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CFileStream.cpp">
|
||||
@@ -731,5 +734,8 @@
|
||||
<ClCompile Include="AntiAliasTable.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BitmapImage.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@@ -16,7 +16,7 @@ namespace PortabilityLayer
|
||||
}
|
||||
|
||||
struct PixMap;
|
||||
struct Picture;
|
||||
struct BitmapImage;
|
||||
struct Point;
|
||||
struct Rect;
|
||||
struct IGpDisplayDriver;
|
||||
@@ -88,7 +88,7 @@ struct DrawSurface final
|
||||
int32_t MeasureFontAscender();
|
||||
int32_t MeasureFontLineGap();
|
||||
|
||||
void DrawPicture(THandle<Picture> pictHandle, const Rect &rect);
|
||||
void DrawPicture(THandle<BitmapImage> pictHandle, const Rect &rect);
|
||||
|
||||
void SetPattern8x8(const uint8_t *pattern);
|
||||
void ClearPattern();
|
||||
@@ -98,8 +98,6 @@ struct DrawSurface final
|
||||
Rect GetClipRect() const;
|
||||
void SetClipRect(const Rect &rect);
|
||||
|
||||
void RegenerateAATable(const PortabilityLayer::RGBAColor &color, const PortabilityLayer::RGBAColor *paletteColors, size_t numColors);
|
||||
|
||||
// Must be the first item
|
||||
PortabilityLayer::QDPort m_port;
|
||||
|
||||
|
@@ -14,14 +14,21 @@ namespace PortabilityLayer
|
||||
}
|
||||
|
||||
bool QDPictHeader::Load(IOStream *stream)
|
||||
{
|
||||
GP_STATIC_ASSERT(sizeof(Picture) == 10);
|
||||
{
|
||||
struct PictHeader
|
||||
{
|
||||
uint8_t m_size[2];
|
||||
|
||||
BERect m_rect;
|
||||
};
|
||||
|
||||
Picture pictHeader;
|
||||
if (stream->Read(&pictHeader, sizeof(Picture)) != sizeof(Picture))
|
||||
GP_STATIC_ASSERT(sizeof(PictHeader) == 10);
|
||||
|
||||
PictHeader pictHeader;
|
||||
if (stream->Read(&pictHeader, sizeof(PictHeader)) != sizeof(PictHeader))
|
||||
return false;
|
||||
|
||||
m_frame = pictHeader.picFrame.ToRect();
|
||||
m_frame = pictHeader.m_rect.ToRect();
|
||||
if (!m_frame.IsValid())
|
||||
return false;
|
||||
|
||||
@@ -67,4 +74,4 @@ namespace PortabilityLayer
|
||||
{
|
||||
return m_frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -119,9 +119,9 @@ namespace PortabilityLayer
|
||||
|
||||
uint8_t StandardPalette::MapColorAnalyticTruncated(unsigned int r, unsigned int g, unsigned int b)
|
||||
{
|
||||
if (g <= 1 && g <= 1)
|
||||
if (g <= 1 && b <= 1)
|
||||
{
|
||||
if (b <= 1)
|
||||
if (r <= 1)
|
||||
{
|
||||
// Special case low gray scale
|
||||
return 255 - b;
|
||||
|
@@ -5,6 +5,8 @@
|
||||
#include "PLBigEndian.h"
|
||||
#include "RGBAColor.h"
|
||||
|
||||
struct BitmapImage;
|
||||
|
||||
struct Point
|
||||
{
|
||||
int16_t v;
|
||||
@@ -63,13 +65,6 @@ struct BERegion
|
||||
BERect rect;
|
||||
};
|
||||
|
||||
struct Picture
|
||||
{
|
||||
uint8_t sizeLowBytes[2]; // Low-order bytes of size, deprecated
|
||||
|
||||
BERect picFrame;
|
||||
};
|
||||
|
||||
struct BEBitMap
|
||||
{
|
||||
BERect m_bounds;
|
||||
|
@@ -163,24 +163,30 @@ namespace PortabilityLayer
|
||||
return nullptr;
|
||||
|
||||
const size_t centralDirSize = eocd.m_centralDirectorySizeBytes;
|
||||
void *centralDirImage = mm->Alloc(centralDirSize);
|
||||
if (!centralDirImage)
|
||||
return nullptr;
|
||||
void *centralDirImage = nullptr;
|
||||
ZipCentralDirectoryFileHeader **centralDirFiles = nullptr;
|
||||
|
||||
const size_t numFiles = eocd.m_numCentralDirRecords;
|
||||
|
||||
ZipCentralDirectoryFileHeader **centralDirFiles = static_cast<ZipCentralDirectoryFileHeader **>(mm->Alloc(sizeof(ZipCentralDirectoryFileHeader*) * numFiles));
|
||||
if (!centralDirFiles)
|
||||
if (centralDirSize > 0)
|
||||
{
|
||||
mm->Release(centralDirImage);
|
||||
return nullptr;
|
||||
}
|
||||
centralDirImage = mm->Alloc(centralDirSize);
|
||||
if (!centralDirImage)
|
||||
return nullptr;
|
||||
|
||||
if (stream->Read(centralDirImage, centralDirSize) != centralDirSize)
|
||||
{
|
||||
mm->Release(centralDirFiles);
|
||||
mm->Release(centralDirImage);
|
||||
return nullptr;
|
||||
centralDirFiles = static_cast<ZipCentralDirectoryFileHeader **>(mm->Alloc(sizeof(ZipCentralDirectoryFileHeader*) * numFiles));
|
||||
if (!centralDirFiles)
|
||||
{
|
||||
mm->Release(centralDirImage);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stream->Read(centralDirImage, centralDirSize) != centralDirSize)
|
||||
{
|
||||
mm->Release(centralDirFiles);
|
||||
mm->Release(centralDirImage);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool failed = false;
|
||||
@@ -246,7 +252,8 @@ namespace PortabilityLayer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
qsort(centralDirFiles, numFiles, sizeof(ZipCentralDirectoryFileHeader*), ZipDirectorySortPredicate);
|
||||
if (numFiles)
|
||||
qsort(centralDirFiles, numFiles, sizeof(ZipCentralDirectoryFileHeader*), ZipDirectorySortPredicate);
|
||||
|
||||
for (size_t i = 1; i < numFiles; i++)
|
||||
{
|
||||
|
Reference in New Issue
Block a user