aboutsummaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
authorStrangerke2012-10-10 08:26:41 +0200
committerStrangerke2012-10-10 08:26:41 +0200
commitb164cbb571fc4e0f2a6f002760a851d8ac592540 (patch)
tree4d25f2e1f8241f6f3352fd9fb1135f5faa36dfd4 /graphics
parentb2f2f8d7b08b40e43702e8db325f8136066f10be (diff)
parent1e200620d673af4acdd2d128ed6e390df001aacf (diff)
downloadscummvm-rg350-b164cbb571fc4e0f2a6f002760a851d8ac592540.tar.gz
scummvm-rg350-b164cbb571fc4e0f2a6f002760a851d8ac592540.tar.bz2
scummvm-rg350-b164cbb571fc4e0f2a6f002760a851d8ac592540.zip
Merge branch 'master' of github.com:scummvm/scummvm into mortevielle
Conflicts: base/plugins.cpp configure
Diffstat (limited to 'graphics')
-rw-r--r--graphics/VectorRenderer.cpp2
-rw-r--r--graphics/VectorRenderer.h2
-rw-r--r--graphics/VectorRendererSpec.cpp68
-rw-r--r--graphics/conversion.cpp202
-rw-r--r--graphics/conversion.h19
-rw-r--r--graphics/cursorman.cpp20
-rw-r--r--graphics/cursorman.h14
-rw-r--r--graphics/decoders/bmp.cpp43
-rw-r--r--graphics/decoders/bmp.h11
-rw-r--r--graphics/decoders/image_decoder.h15
-rw-r--r--graphics/decoders/jpeg.cpp4
-rw-r--r--graphics/decoders/jpeg.h7
-rw-r--r--graphics/decoders/pcx.cpp213
-rw-r--r--graphics/decoders/pcx.h68
-rw-r--r--graphics/decoders/pict.cpp46
-rw-r--r--graphics/decoders/pict.h14
-rw-r--r--graphics/decoders/png.cpp574
-rw-r--r--graphics/decoders/png.h77
-rw-r--r--graphics/decoders/tga.cpp427
-rw-r--r--graphics/decoders/tga.h98
-rw-r--r--graphics/fontman.cpp36
-rw-r--r--graphics/fontman.h5
-rw-r--r--graphics/fonts/bdf.h2
-rw-r--r--graphics/fonts/consolefont.cpp2
-rw-r--r--graphics/fonts/newfont.cpp2
-rw-r--r--graphics/fonts/newfont_big.cpp2
-rw-r--r--graphics/fonts/ttf.cpp18
-rw-r--r--graphics/fonts/ttf.h4
-rw-r--r--graphics/iff.cpp23
-rw-r--r--graphics/module.mk4
-rw-r--r--graphics/pixelformat.h2
-rw-r--r--graphics/primitives.cpp62
-rw-r--r--graphics/primitives.h2
-rw-r--r--graphics/scaler.cpp9
-rw-r--r--graphics/scaler/aspect.cpp72
-rw-r--r--graphics/sjis.h2
-rw-r--r--graphics/surface.cpp78
-rw-r--r--graphics/surface.h30
-rw-r--r--graphics/wincursor.cpp40
-rw-r--r--graphics/wincursor.h42
-rw-r--r--graphics/yuv_to_rgb.cpp259
-rw-r--r--graphics/yuv_to_rgb.h99
42 files changed, 1795 insertions, 924 deletions
diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp
index 30ef9eeeeb..f426dd8c41 100644
--- a/graphics/VectorRenderer.cpp
+++ b/graphics/VectorRenderer.cpp
@@ -80,7 +80,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect &
case Graphics::DrawStep::kVectorAlignManual:
if (step.x >= 0)
in_x = area.left + step.x + step.padding.left;
- else
+ else
in_x = area.left + area.width() + step.x + step.padding.left; // value relative to the opposite corner.
break;
diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index e98f4aa761..0467cac946 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -55,7 +55,7 @@ struct DrawStep {
bool autoWidth, autoHeight;
int16 x, y, w, h; /**< width, height and position, if not measured automatically.
negative values mean counting from the opposite direction */
-
+
Common::Rect padding;
enum VectorAlignment {
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 7817725664..6a3ee306a5 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -369,12 +369,12 @@ gradientFill(PixelType *ptr, int width, int x, int y) {
int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize;
// Dithering:
- // +--+ +--+ +--+ +--+
- // | | | | | *| | *|
- // | | | *| |* | |**|
- // +--+ +--+ +--+ +--+
+ // +--+ +--+ +--+ +--+
+ // | | | | | *| | *|
+ // | | | *| |* | |**|
+ // +--+ +--+ +--+ +--+
// 0 1 2 3
- if (grad == 0 ||
+ if (grad == 0 ||
_gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change
stripSize < 2) { // the stip is small
colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]);
@@ -422,8 +422,8 @@ void VectorRendererSpec<PixelType>::
copyFrame(OSystem *sys, const Common::Rect &r) {
sys->copyRectToOverlay(
- (const OverlayColor *)_activeSurface->getBasePtr(r.left, r.top),
- _activeSurface->pitch / _activeSurface->format.bytesPerPixel,
+ _activeSurface->getBasePtr(r.left, r.top),
+ _activeSurface->pitch,
r.left, r.top, r.width(), r.height()
);
}
@@ -873,7 +873,7 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
case kTriangleDown:
drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode);
break;
-
+
case kTriangleLeft:
case kTriangleRight:
case kTriangleAuto:
@@ -1206,14 +1206,14 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
pitch = -pitch;
y1 += h;
}
-
+
PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
PixelType *floor = ptr_right - 1;
PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + w, y1);
int x2 = x1 + w / 2;
int y2 = y1 + h;
-
+
#if FIXED_POINT
int dx = (x2 - x1) << 8;
int dy = (y2 - y1) << 8;
@@ -1227,7 +1227,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
#endif
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
-
+
#if FIXED_POINT
int gradient = (dy << 8) / dx;
int intery = (y1 << 8) + gradient;
@@ -1250,7 +1250,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_right += pitch;
intery += gradient;
-
+
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@@ -1262,16 +1262,16 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(intery));
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(intery));
blendPixelPtr(ptr_left, color, rfpart(intery));
break;
}
}
-
+
return;
}
-
+
#if FIXED_POINT
if (abs(dx) < abs(dy)) {
#else
@@ -1280,7 +1280,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_left--;
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
-
+
#if FIXED_POINT
int gradient = (dx << 8) / (dy + 0x100);
int interx = (x1 << 8) + gradient;
@@ -1303,7 +1303,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
ptr_right += pitch;
interx += gradient;
-
+
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@@ -1315,18 +1315,18 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(interx));
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
}
}
-
+
return;
}
-
+
ptr_left--;
-
+
while (floor++ != ptr_left)
blendPixelPtr(floor, color, 50);
@@ -1341,12 +1341,12 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
for (int y = y1 + 1; y < y2; y++) {
ptr_right++;
ptr_left--;
-
+
ptr_left += pitch;
ptr_right += pitch;
interx += gradient;
-
+
switch (fill_m) {
case kFillDisabled:
*ptr_left = *ptr_right = color;
@@ -1358,13 +1358,13 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h));
blendPixelPtr(ptr_right, color, rfpart(interx));
blendPixelPtr(ptr_left, color, rfpart(interx));
break;
}
}
-
+
}
/** VERTICAL TRIANGLE DRAWING - FAST VERSION FOR SQUARED TRIANGLES */
@@ -1372,12 +1372,12 @@ template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) {
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
-
+
if (!inverted) {
pitch = -pitch;
y1 += size;
}
-
+
int gradient_h = 0;
PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + size, y1);
@@ -1388,9 +1388,9 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto
int signX = x1 < x2 ? 1 : -1;
int signY = y1 < y2 ? 1 : -1;
int error = deltaX - deltaY;
-
+
colorFill<PixelType>(ptr_right, ptr_left, color);
-
+
while (1) {
switch (fill_m) {
case kFillDisabled:
@@ -1401,22 +1401,22 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto
colorFill<PixelType>(ptr_right, ptr_left, color);
break;
case kFillGradient:
- colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size));
+ colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size));
break;
}
-
+
if (x1 == x2 && y1 == y2)
break;
-
+
int error2 = error * 2;
-
+
if (error2 > -deltaY) {
error -= deltaY;
x1 += signX;
ptr_right += signX;
ptr_left += -signX;
}
-
+
if (error2 < deltaX) {
error += deltaX;
y1 += signY;
diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp
index 713a06ea74..2da8b6f0ce 100644
--- a/graphics/conversion.cpp
+++ b/graphics/conversion.cpp
@@ -22,123 +22,143 @@
#include "graphics/conversion.h"
#include "graphics/pixelformat.h"
+#include "common/endian.h"
+
namespace Graphics {
// TODO: YUV to RGB conversion function
+namespace {
+
+template<typename SrcColor, typename DstColor, bool backward>
+inline void crossBlitLogic(byte *dst, const byte *src, const uint w, const uint h,
+ const PixelFormat &srcFmt, const PixelFormat &dstFmt,
+ const uint srcDelta, const uint dstDelta) {
+ for (uint y = 0; y < h; ++y) {
+ for (uint x = 0; x < w; ++x) {
+ const uint32 color = *(const SrcColor *)src;
+ byte a, r, g, b;
+ srcFmt.colorToARGB(color, a, r, g, b);
+ *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b);
+
+ if (backward) {
+ src -= sizeof(SrcColor);
+ dst -= sizeof(DstColor);
+ } else {
+ src += sizeof(SrcColor);
+ dst += sizeof(DstColor);
+ }
+ }
+
+ if (backward) {
+ src -= srcDelta;
+ dst -= dstDelta;
+ } else {
+ src += srcDelta;
+ dst += dstDelta;
+ }
+ }
+}
+
+template<typename DstColor, bool backward>
+inline void crossBlitLogic3BppSource(byte *dst, const byte *src, const uint w, const uint h,
+ const PixelFormat &srcFmt, const PixelFormat &dstFmt,
+ const uint srcDelta, const uint dstDelta) {
+ uint32 color;
+ byte r, g, b, a;
+ uint8 *col = (uint8 *)&color;
+#ifdef SCUMM_BIG_ENDIAN
+ col++;
+#endif
+ for (uint y = 0; y < h; ++y) {
+ for (uint x = 0; x < w; ++x) {
+ memcpy(col, src, 3);
+ srcFmt.colorToARGB(color, a, r, g, b);
+ *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b);
+
+ if (backward) {
+ src -= 3;
+ dst -= sizeof(DstColor);
+ } else {
+ src += 3;
+ dst += sizeof(DstColor);
+ }
+ }
+
+ if (backward) {
+ src -= srcDelta;
+ dst -= dstDelta;
+ } else {
+ src += srcDelta;
+ dst += dstDelta;
+ }
+ }
+}
+
+} // End of anonymous namespace
+
// Function to blit a rect from one color format to another
-bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch,
- int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) {
+bool crossBlit(byte *dst, const byte *src,
+ const uint dstPitch, const uint srcPitch,
+ const uint w, const uint h,
+ const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) {
// Error out if conversion is impossible
if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1)
- || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel)
- || (srcFmt.bytesPerPixel > dstFmt.bytesPerPixel))
+ || (dstFmt.bytesPerPixel == 3)
+ || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel))
return false;
// Don't perform unnecessary conversion
if (srcFmt == dstFmt) {
- if (dst == src)
- return true;
- if (dstpitch == srcpitch && ((w * dstFmt.bytesPerPixel) == dstpitch)) {
- memcpy(dst,src,dstpitch * h);
- return true;
- } else {
- for (int i = 0; i < h; i++) {
- memcpy(dst,src,w * dstFmt.bytesPerPixel);
- dst += dstpitch;
- src += srcpitch;
+ if (dst != src) {
+ if (dstPitch == srcPitch && ((w * dstFmt.bytesPerPixel) == dstPitch)) {
+ memcpy(dst, src, dstPitch * h);
+ } else {
+ for (uint i = 0; i < h; ++i) {
+ memcpy(dst, src, w * dstFmt.bytesPerPixel);
+ dst += dstPitch;
+ src += srcPitch;
+ }
}
- return true;
}
+
+ return true;
}
// Faster, but larger, to provide optimized handling for each case.
- int srcDelta, dstDelta;
- srcDelta = (srcpitch - w * srcFmt.bytesPerPixel);
- dstDelta = (dstpitch - w * dstFmt.bytesPerPixel);
+ const uint srcDelta = (srcPitch - w * srcFmt.bytesPerPixel);
+ const uint dstDelta = (dstPitch - w * dstFmt.bytesPerPixel);
// TODO: optimized cases for dstDelta of 0
- uint8 r, g, b, a;
if (dstFmt.bytesPerPixel == 2) {
- uint16 color;
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++, src += 2, dst += 2) {
- color = *(const uint16 *)src;
- srcFmt.colorToARGB(color, a, r, g, b);
- color = dstFmt.ARGBToColor(a, r, g, b);
- *(uint16 *)dst = color;
- }
- src += srcDelta;
- dst += dstDelta;
- }
- } else if (dstFmt.bytesPerPixel == 3) {
- uint32 color;
- uint8 *col = (uint8 *) &color;
-#ifdef SCUMM_BIG_ENDIAN
- col++;
-#endif
if (srcFmt.bytesPerPixel == 2) {
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++, src += 2, dst += 3) {
- color = *(const uint16 *)src;
- srcFmt.colorToARGB(color, a, r, g, b);
- color = dstFmt.ARGBToColor(a, r, g, b);
- memcpy(dst, col, 3);
- }
- src += srcDelta;
- dst += dstDelta;
- }
+ crossBlitLogic<uint16, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
+ } else if (srcFmt.bytesPerPixel == 3) {
+ crossBlitLogic3BppSource<uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
} else {
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++, src += 3, dst += 3) {
- memcpy(col, src, 3);
- srcFmt.colorToARGB(color, a, r, g, b);
- color = dstFmt.ARGBToColor(a, r, g, b);
- memcpy(dst, col, 3);
- }
- src += srcDelta;
- dst += dstDelta;
- }
+ crossBlitLogic<uint32, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
}
} else if (dstFmt.bytesPerPixel == 4) {
- uint32 color;
if (srcFmt.bytesPerPixel == 2) {
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++, src += 2, dst += 4) {
- color = *(const uint16 *)src;
- srcFmt.colorToARGB(color, a, r, g, b);
- color = dstFmt.ARGBToColor(a, r, g, b);
- *(uint32 *)dst = color;
- }
- src += srcDelta;
- dst += dstDelta;
- }
+ // We need to blit the surface from bottom right to top left here.
+ // This is neeeded, because when we convert to the same memory
+ // buffer copying the surface from top left to bottom right would
+ // overwrite the source, since we have more bits per destination
+ // color than per source color.
+ dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel;
+ src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel;
+ crossBlitLogic<uint16, uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
} else if (srcFmt.bytesPerPixel == 3) {
- uint8 *col = (uint8 *)&color;
-#ifdef SCUMM_BIG_ENDIAN
- col++;
-#endif
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++, src += 2, dst += 4) {
- memcpy(col, src, 3);
- srcFmt.colorToARGB(color, a, r, g, b);
- color = dstFmt.ARGBToColor(a, r, g, b);
- *(uint32 *)dst = color;
- }
- src += srcDelta;
- dst += dstDelta;
- }
+ // We need to blit the surface from bottom right to top left here.
+ // This is neeeded, because when we convert to the same memory
+ // buffer copying the surface from top left to bottom right would
+ // overwrite the source, since we have more bits per destination
+ // color than per source color.
+ dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel;
+ src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel;
+ crossBlitLogic3BppSource<uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
} else {
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++, src += 4, dst += 4) {
- color = *(const uint32 *)src;
- srcFmt.colorToARGB(color, a, r, g, b);
- color = dstFmt.ARGBToColor(a, r, g, b);
- *(uint32 *)dst = color;
- }
- src += srcDelta;
- dst += dstDelta;
- }
+ crossBlitLogic<uint32, uint32, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta);
}
} else {
return false;
diff --git a/graphics/conversion.h b/graphics/conversion.h
index 6babc763e2..28e64a94fb 100644
--- a/graphics/conversion.h
+++ b/graphics/conversion.h
@@ -59,15 +59,18 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) {
* @return true if conversion completes successfully,
* false if there is an error.
*
- * @note This implementation currently arbitrarily requires that the
- * destination's format have at least as high a bytedepth as
- * the source's.
- * @note This can convert a rectangle in place, if the source and
- * destination format have the same bytedepth.
- *
+ * @note Blitting to a 3Bpp destination is not supported
+ * @note This can convert a surface in place, regardless of the
+ * source and destination format, as long as there is enough
+ * space for the destination. The dstPitch / srcPitch ratio
+ * must at least equal the dstBpp / srcBpp ratio for
+ * dstPitch >= srcPitch and at most dstBpp / srcBpp for
+ * dstPitch < srcPitch though.
*/
-bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch,
- int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt);
+bool crossBlit(byte *dst, const byte *src,
+ const uint dstPitch, const uint srcPitch,
+ const uint w, const uint h,
+ const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt);
} // End of namespace Graphics
diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp
index 425714ea34..c818101645 100644
--- a/graphics/cursorman.cpp
+++ b/graphics/cursorman.cpp
@@ -55,14 +55,14 @@ bool CursorManager::showMouse(bool visible) {
return g_system->showMouse(visible);
}
-void CursorManager::pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) {
- Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format);
+void CursorManager::pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
+ Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
cur->_visible = isVisible();
_cursorStack.push(cur);
if (buf) {
- g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format);
+ g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
}
}
@@ -75,7 +75,7 @@ void CursorManager::popCursor() {
if (!_cursorStack.empty()) {
cur = _cursorStack.top();
- g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_targetScale, &cur->_format);
+ g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_dontScale, &cur->_format);
}
g_system->showMouse(isVisible());
@@ -98,10 +98,10 @@ void CursorManager::popAllCursors() {
g_system->showMouse(isVisible());
}
-void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) {
+void CursorManager::replaceCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
if (_cursorStack.empty()) {
- pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format);
+ pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
return;
}
@@ -131,7 +131,7 @@ void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX,
cur->_hotspotX = hotspotX;
cur->_hotspotY = hotspotY;
cur->_keycolor = keycolor;
- cur->_targetScale = targetScale;
+ cur->_dontScale = dontScale;
#ifdef USE_RGB_COLOR
if (format)
cur->_format = *format;
@@ -139,7 +139,7 @@ void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX,
cur->_format = Graphics::PixelFormat::createFormatCLUT8();
#endif
- g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format);
+ g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format);
}
bool CursorManager::supportsCursorPalettes() {
@@ -225,7 +225,7 @@ void CursorManager::replaceCursorPalette(const byte *colors, uint start, uint nu
}
}
-CursorManager::Cursor::Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) {
+CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
#ifdef USE_RGB_COLOR
if (!format)
_format = Graphics::PixelFormat::createFormatCLUT8();
@@ -245,7 +245,7 @@ CursorManager::Cursor::Cursor(const byte *data, uint w, uint h, int hotspotX, in
_height = h;
_hotspotX = hotspotX;
_hotspotY = hotspotY;
- _targetScale = targetScale;
+ _dontScale = dontScale;
}
CursorManager::Cursor::~Cursor() {
diff --git a/graphics/cursorman.h b/graphics/cursorman.h
index 543a5d0a5c..66e8d1ba56 100644
--- a/graphics/cursorman.h
+++ b/graphics/cursorman.h
@@ -63,14 +63,15 @@ public:
* @param hotspotY the hotspot Y coordinate
* @param keycolor the color value for the transparent color. This may not exceed
* the maximum color value as defined by format.
- * @param targetScale the scale for which the cursor is designed
+ * @param dontScale Whether the cursor should never be scaled. An exception are high ppi displays, where the cursor
+ * would be too small to notice otherwise, these are allowed to scale the cursor anyway.
* @param format a pointer to the pixel format which the cursor graphic uses,
* CLUT8 will be used if this is NULL or not specified.
* @note It is ok for the buffer to be a NULL pointer. It is sometimes
* useful to push a "dummy" cursor and modify it later. The
* cursor will be added to the stack, but not to the backend.
*/
- void pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL);
+ void pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
/**
* Pop a cursor from the stack, and restore the previous one to the
@@ -90,11 +91,12 @@ public:
* @param hotspotY the hotspot Y coordinate
* @param keycolor the color value for the transparent color. This may not exceed
* the maximum color value as defined by format.
- * @param targetScale the scale for which the cursor is designed
+ * @param dontScale Whether the cursor should never be scaled. An exception are high ppi displays, where the cursor
+ * would be too small to notice otherwise, these are allowed to scale the cursor anyway.
* @param format a pointer to the pixel format which the cursor graphic uses,
* CLUT8 will be used if this is NULL or not specified.
*/
- void replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL);
+ void replaceCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
/**
* Pop all of the cursors and cursor palettes from their respective stacks.
@@ -175,11 +177,11 @@ private:
int _hotspotY;
uint32 _keycolor;
Graphics::PixelFormat _format;
- int _targetScale;
+ bool _dontScale;
uint _size;
- Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL);
+ Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
~Cursor();
};
diff --git a/graphics/decoders/bmp.cpp b/graphics/decoders/bmp.cpp
index 0d44881d7c..bcfd0abbda 100644
--- a/graphics/decoders/bmp.cpp
+++ b/graphics/decoders/bmp.cpp
@@ -31,6 +31,7 @@ namespace Graphics {
BitmapDecoder::BitmapDecoder() {
_surface = 0;
_palette = 0;
+ _paletteColorCount = 0;
}
BitmapDecoder::~BitmapDecoder() {
@@ -44,6 +45,7 @@ void BitmapDecoder::destroy() {
}
delete[] _palette; _palette = 0;
+ _paletteColorCount = 0;
}
bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
@@ -80,7 +82,7 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
/* uint16 planes = */ stream.readUint16LE();
uint16 bitsPerPixel = stream.readUint16LE();
- if (bitsPerPixel != 8 && bitsPerPixel != 24) {
+ if (bitsPerPixel != 8 && bitsPerPixel != 24 && bitsPerPixel != 32) {
warning("%dbpp bitmaps not supported", bitsPerPixel);
return false;
}
@@ -95,16 +97,16 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
/* uint32 imageSize = */ stream.readUint32LE();
/* uint32 pixelsPerMeterX = */ stream.readUint32LE();
/* uint32 pixelsPerMeterY = */ stream.readUint32LE();
- uint32 colorsUsed = stream.readUint32LE();
+ _paletteColorCount = stream.readUint32LE();
/* uint32 colorsImportant = */ stream.readUint32LE();
- if (colorsUsed == 0)
- colorsUsed = 256;
-
if (bitsPerPixel == 8) {
+ if (_paletteColorCount == 0)
+ _paletteColorCount = 256;
+
// Read the palette
- _palette = new byte[colorsUsed * 3];
- for (uint16 i = 0; i < colorsUsed; i++) {
+ _palette = new byte[_paletteColorCount * 3];
+ for (uint16 i = 0; i < _paletteColorCount; i++) {
_palette[i * 3 + 2] = stream.readByte();
_palette[i * 3 + 1] = stream.readByte();
_palette[i * 3 + 0] = stream.readByte();
@@ -117,8 +119,8 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
- // BGRA for 24bpp
- if (bitsPerPixel == 24)
+ // BGRA for 24bpp and 32 bpp
+ if (bitsPerPixel == 24 || bitsPerPixel == 32)
format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0);
_surface = new Graphics::Surface();
@@ -134,7 +136,24 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
stream.read(dst + (height - i - 1) * width, width);
stream.skip(extraDataLength);
}
- } else {
+ } else if (bitsPerPixel == 24) {
+ byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch;
+
+ for (int32 i = 0; i < height; i++) {
+ for (uint32 j = 0; j < width; j++) {
+ byte b = stream.readByte();
+ byte g = stream.readByte();
+ byte r = stream.readByte();
+ uint32 color = format.RGBToColor(r, g, b);
+
+ *((uint32 *)dst) = color;
+ dst += format.bytesPerPixel;
+ }
+
+ stream.skip(extraDataLength);
+ dst -= _surface->pitch * 2;
+ }
+ } else { // 32 bpp
byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch;
for (int32 i = 0; i < height; i++) {
@@ -142,6 +161,10 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
byte b = stream.readByte();
byte g = stream.readByte();
byte r = stream.readByte();
+ // Ignore the last byte, as in v3 it is unused
+ // and should thus NOT be used as alpha.
+ // ref: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376%28v=vs.85%29.aspx
+ stream.readByte();
uint32 color = format.RGBToColor(r, g, b);
*((uint32 *)dst) = color;
diff --git a/graphics/decoders/bmp.h b/graphics/decoders/bmp.h
index e11b12fad6..59da682e4d 100644
--- a/graphics/decoders/bmp.h
+++ b/graphics/decoders/bmp.h
@@ -19,6 +19,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+/**
+ * @file
+ * Image decoder used in engines:
+ * - hugo
+ * - mohawk
+ */
+
#ifndef GRAPHICS_DECODERS_BMP_H
#define GRAPHICS_DECODERS_BMP_H
@@ -44,11 +51,13 @@ public:
void destroy();
virtual bool loadStream(Common::SeekableReadStream &stream);
virtual const Surface *getSurface() const { return _surface; }
- virtual const byte *getPalette() { return _palette; }
+ const byte *getPalette() const { return _palette; }
+ uint16 getPaletteColorCount() const { return _paletteColorCount; }
private:
Surface *_surface;
byte *_palette;
+ uint16 _paletteColorCount;
};
} // End of namespace Graphics
diff --git a/graphics/decoders/image_decoder.h b/graphics/decoders/image_decoder.h
index e768f7f9a2..49e31c6e3a 100644
--- a/graphics/decoders/image_decoder.h
+++ b/graphics/decoders/image_decoder.h
@@ -75,9 +75,22 @@ public:
* until destroy() or loadStream() is called, or until this ImageDecoder's
* destructor is called.
*
- * @return the decoded palette, or 0 if no palette is present
+ * The palette's format is the same as PaletteManager's palette
+ * (interleaved RGB values).
+ *
+ * @return the decoded palette, or undefined if no palette is present
*/
virtual const byte *getPalette() const { return 0; }
+
+ /**
+ * Query if the decoded image has a palette.
+ */
+ virtual bool hasPalette() const { return getPaletteColorCount() != 0; }
+
+ /** Return the starting index of the palette. */
+ virtual byte getPaletteStartIndex() const { return 0; }
+ /** Return the number of colors in the palette. */
+ virtual uint16 getPaletteColorCount() const { return 0; }
};
} // End of namespace Graphics
diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
index a871377ca1..08bc1f7a3d 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -81,7 +81,7 @@ const Surface *JPEGDecoder::getSurface() const {
const Graphics::Surface *uComponent = getComponent(2);
const Graphics::Surface *vComponent = getComponent(3);
- convertYUV444ToRGB(_rgbSurface, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
+ YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
return _rgbSurface;
}
@@ -452,7 +452,7 @@ bool JPEGDecoder::readSOS() {
_bitsNumber = 0;
for (byte i = 0; i < _numScanComp; i++)
- _scanComp[i]->DCpredictor = 0;
+ _scanComp[i]->DCpredictor = 0;
}
}
}
diff --git a/graphics/decoders/jpeg.h b/graphics/decoders/jpeg.h
index c566d5ad21..c74aa57ca1 100644
--- a/graphics/decoders/jpeg.h
+++ b/graphics/decoders/jpeg.h
@@ -20,6 +20,13 @@
*
*/
+/**
+ * @file
+ * Image decoder used in engines:
+ * - groovie
+ * - mohawk
+ */
+
#ifndef GRAPHICS_JPEG_H
#define GRAPHICS_JPEG_H
diff --git a/graphics/decoders/pcx.cpp b/graphics/decoders/pcx.cpp
new file mode 100644
index 0000000000..1250398c73
--- /dev/null
+++ b/graphics/decoders/pcx.cpp
@@ -0,0 +1,213 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+#include "graphics/decoders/pcx.h"
+
+/**
+ * Based on the PCX specs:
+ * http://www.fileformat.info/format/pcx/spec/a10e75307b3a4cc49c3bbe6db4c41fa2/view.htm
+ * and the PCX decoder of FFmpeg (libavcodec/pcx.c):
+ * http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavcodec/pcx.c
+ */
+
+namespace Graphics {
+
+PCXDecoder::PCXDecoder() {
+ _surface = 0;
+ _palette = 0;
+ _paletteColorCount = 0;
+}
+
+PCXDecoder::~PCXDecoder() {
+ destroy();
+}
+
+void PCXDecoder::destroy() {
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ _surface = 0;
+ }
+
+ delete[] _palette;
+ _palette = 0;
+ _paletteColorCount = 0;
+}
+
+bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) {
+ destroy();
+
+ if (stream.readByte() != 0x0a) // ZSoft PCX
+ return false;
+
+ byte version = stream.readByte(); // 0 - 5
+ if (version > 5)
+ return false;
+
+ bool compressed = stream.readByte(); // encoding, 1 = run length encoding
+ byte bitsPerPixel = stream.readByte(); // 1, 2, 4 or 8
+
+ // Window
+ uint16 xMin = stream.readUint16LE();
+ uint16 yMin = stream.readUint16LE();
+ uint16 xMax = stream.readUint16LE();
+ uint16 yMax = stream.readUint16LE();
+
+ uint16 width = xMax - xMin + 1;
+ uint16 height = yMax - yMin + 1;
+
+ if (xMax < xMin || yMax < yMin) {
+ warning("Invalid PCX image dimensions");
+ return false;
+ }
+
+ stream.skip(4); // HDpi, VDpi
+
+ // Read the EGA palette (colormap)
+ _palette = new byte[16 * 3];
+ for (uint16 i = 0; i < 16; i++) {
+ _palette[i * 3 + 0] = stream.readByte();
+ _palette[i * 3 + 1] = stream.readByte();
+ _palette[i * 3 + 2] = stream.readByte();
+ }
+
+ if (stream.readByte() != 0) // reserved, should be set to 0
+ return false;
+
+ byte nPlanes = stream.readByte();
+ uint16 bytesPerLine = stream.readUint16LE();
+ uint16 bytesPerscanLine = nPlanes * bytesPerLine;
+
+ if (bytesPerscanLine < width * bitsPerPixel * nPlanes / 8) {
+ warning("PCX data is corrupted");
+ return false;
+ }
+
+ stream.skip(60); // PaletteInfo, HscreenSize, VscreenSize, Filler
+
+ _surface = new Graphics::Surface();
+
+ byte *scanLine = new byte[bytesPerscanLine];
+ byte *dst;
+ int x, y;
+
+ if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp
+ Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+ _surface->create(width, height, format);
+ dst = (byte *)_surface->pixels;
+ _paletteColorCount = 0;
+
+ for (y = 0; y < height; y++) {
+ decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
+
+ for (x = 0; x < width; x++) {
+ byte b = scanLine[x];
+ byte g = scanLine[x + bytesPerLine];
+ byte r = scanLine[x + (bytesPerLine << 1)];
+ uint32 color = format.RGBToColor(r, g, b);
+
+ *((uint32 *)dst) = color;
+ dst += format.bytesPerPixel;
+ }
+ }
+ } else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed
+ _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ dst = (byte *)_surface->pixels;
+ _paletteColorCount = 16;
+
+ for (y = 0; y < height; y++, dst += _surface->pitch) {
+ decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
+ memcpy(dst, scanLine, width);
+ }
+
+ if (version == 5) {
+ if (stream.readByte() != 12) {
+ warning("Expected a palette after the PCX image data");
+ delete[] scanLine;
+ return false;
+ }
+
+ // Read the VGA palette
+ delete[] _palette;
+ _palette = new byte[256 * 3];
+ for (uint16 i = 0; i < 256; i++) {
+ _palette[i * 3 + 0] = stream.readByte();
+ _palette[i * 3 + 1] = stream.readByte();
+ _palette[i * 3 + 2] = stream.readByte();
+ }
+
+ _paletteColorCount = 256;
+ }
+ } else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors
+ _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ dst = (byte *)_surface->pixels;
+ _paletteColorCount = 16;
+
+ for (y = 0; y < height; y++, dst += _surface->pitch) {
+ decodeRLE(stream, scanLine, bytesPerscanLine, compressed);
+
+ for (x = 0; x < width; x++) {
+ int m = 0x80 >> (x & 7), v = 0;
+ for (int i = nPlanes - 1; i >= 0; i--) {
+ v <<= 1;
+ v += (scanLine[i * bytesPerLine + (x >> 3)] & m) == 0 ? 0 : 1;
+ }
+ dst[x] = v;
+ }
+ }
+ } else {
+ // Known unsupported case: 1 plane and bpp < 8 (1, 2 or 4)
+ warning("Invalid PCX file (%d planes, %d bpp)", nPlanes, bitsPerPixel);
+ delete[] scanLine;
+ return false;
+ }
+
+ delete[] scanLine;
+
+ return true;
+}
+
+void PCXDecoder::decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerscanLine, bool compressed) {
+ uint32 i = 0;
+ byte run, value;
+
+ if (compressed) {
+ while (i < bytesPerscanLine) {
+ run = 1;
+ value = stream.readByte();
+ if (value >= 0xc0) {
+ run = value & 0x3f;
+ value = stream.readByte();
+ }
+ while (i < bytesPerscanLine && run--)
+ dst[i++] = value;
+ }
+ } else {
+ stream.read(dst, bytesPerscanLine);
+ }
+}
+
+} // End of namespace Graphics
diff --git a/graphics/decoders/pcx.h b/graphics/decoders/pcx.h
new file mode 100644
index 0000000000..b25166b3d9
--- /dev/null
+++ b/graphics/decoders/pcx.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * PCX decoder used in engines:
+ * - dreamweb
+ * - hugo
+ * - queen
+ * - tucker
+ */
+
+#ifndef GRAPHICS_DECODERS_PCX_H
+#define GRAPHICS_DECODERS_PCX_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "graphics/decoders/image_decoder.h"
+
+namespace Common{
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+struct PixelFormat;
+struct Surface;
+
+class PCXDecoder : public ImageDecoder {
+public:
+ PCXDecoder();
+ virtual ~PCXDecoder();
+
+ // ImageDecoder API
+ void destroy();
+ virtual bool loadStream(Common::SeekableReadStream &stream);
+ virtual const Surface *getSurface() const { return _surface; }
+ const byte *getPalette() const { return _palette; }
+ uint16 getPaletteColorCount() const { return _paletteColorCount; }
+
+private:
+ void decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerScanline, bool compressed);
+
+ Surface *_surface;
+ byte *_palette;
+ uint16 _paletteColorCount;
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp
index 957342084e..d35e5c3064 100644
--- a/graphics/decoders/pict.cpp
+++ b/graphics/decoders/pict.cpp
@@ -38,6 +38,7 @@ namespace Graphics {
PICTDecoder::PICTDecoder() {
_outputSurface = 0;
+ _paletteColorCount = 0;
}
PICTDecoder::~PICTDecoder() {
@@ -50,6 +51,8 @@ void PICTDecoder::destroy() {
delete _outputSurface;
_outputSurface = 0;
}
+
+ _paletteColorCount = 0;
}
#define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PICTDecoder::b, c))
@@ -289,18 +292,18 @@ struct PackBitsRectData {
uint16 mode;
};
-void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette) {
+void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette) {
PackBitsRectData packBitsData;
- packBitsData.pixMap = readPixMap(stream, !hasPalette);
+ packBitsData.pixMap = readPixMap(stream, !withPalette);
// Read in the palette if there is one present
- if (hasPalette) {
+ if (withPalette) {
// See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html
stream.readUint32BE(); // seed
stream.readUint16BE(); // flags
- uint16 colorCount = stream.readUint16BE() + 1;
+ _paletteColorCount = stream.readUint16BE() + 1;
- for (uint32 i = 0; i < colorCount; i++) {
+ for (uint32 i = 0; i < _paletteColorCount; i++) {
stream.readUint16BE();
_palette[i * 3] = stream.readUint16BE() >> 8;
_palette[i * 3 + 1] = stream.readUint16BE() >> 8;
@@ -361,14 +364,14 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal
memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h);
break;
case 2:
- // Convert from 16-bit to whatever surface we need
+ // We have a 16-bit surface
_outputSurface->create(width, height, PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
for (uint16 y = 0; y < _outputSurface->h; y++)
for (uint16 x = 0; x < _outputSurface->w; x++)
WRITE_UINT16(_outputSurface->getBasePtr(x, y), READ_UINT16(buffer + (y * _outputSurface->w + x) * 2));
break;
case 3:
- // Convert from 24-bit (planar!) to whatever surface we need
+ // We have a planar 24-bit surface
_outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
for (uint16 y = 0; y < _outputSurface->h; y++) {
for (uint16 x = 0; x < _outputSurface->w; x++) {
@@ -380,15 +383,18 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal
}
break;
case 4:
- // Convert from 32-bit (planar!) to whatever surface we need
+ // We have a planar 32-bit surface
+ // Note that we ignore the alpha channel since it seems to not be correct
+ // Mac OS X does not ignore it, but then displays it incorrectly. Photoshop
+ // does ignore it and displays it correctly.
_outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
for (uint16 y = 0; y < _outputSurface->h; y++) {
for (uint16 x = 0; x < _outputSurface->w; x++) {
- byte r = *(buffer + y * _outputSurface->w * 4 + x);
- byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x);
- byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x);
- byte a = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x);
- *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(r, g, b, a);
+ byte a = 0xFF;
+ byte r = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x);
+ byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x);
+ byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x);
+ *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(a, r, g, b);
}
}
break;
@@ -463,10 +469,10 @@ void PICTDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) {
}
}
-void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette) {
+void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool withPalette) {
// Step through a PackBitsRect/DirectBitsRect function
- if (!hasPalette)
+ if (!withPalette)
stream.readUint32BE();
uint16 rowBytes = stream.readUint16BE();
@@ -486,7 +492,7 @@ void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalet
stream.readUint16BE(); // pixelSize
stream.skip(16);
- if (hasPalette) {
+ if (withPalette) {
stream.readUint32BE();
stream.readUint16BE();
stream.skip((stream.readUint16BE() + 1) * 8);
@@ -537,14 +543,18 @@ void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream)
// Skip the matte and mask
stream.skip(matteSize + maskSize);
-
+
// Now we've reached the image descriptor, so read the relevant data from that
uint32 idStart = stream.pos();
uint32 idSize = stream.readUint32BE();
- stream.skip(40); // miscellaneous stuff
+ uint32 codec = stream.readUint32BE();
+ stream.skip(36); // miscellaneous stuff
uint32 jpegSize = stream.readUint32BE();
stream.skip(idSize - (stream.pos() - idStart)); // more useless stuff
+ if (codec != MKTAG('j', 'p', 'e', 'g'))
+ error("Unhandled CompressedQuickTime format '%s'", tag2str(codec));
+
Common::SeekableSubReadStream jpegStream(&stream, stream.pos(), stream.pos() + jpegSize);
JPEGDecoder jpeg;
diff --git a/graphics/decoders/pict.h b/graphics/decoders/pict.h
index b1e45a6bc1..6f0d86c7a1 100644
--- a/graphics/decoders/pict.h
+++ b/graphics/decoders/pict.h
@@ -20,6 +20,14 @@
*
*/
+/**
+ * @file
+ * Image decoder used in engines:
+ * - mohawk
+ * - pegasus
+ * - sci
+ */
+
#ifndef GRAPHICS_PICT_H
#define GRAPHICS_PICT_H
@@ -50,6 +58,7 @@ public:
void destroy();
const Surface *getSurface() const { return _outputSurface; }
const byte *getPalette() const { return _palette; }
+ uint16 getPaletteColorCount() const { return _paletteColorCount; }
struct PixMap {
uint32 baseAddr;
@@ -74,13 +83,14 @@ public:
private:
Common::Rect _imageRect;
byte _palette[256 * 3];
+ uint16 _paletteColorCount;
Graphics::Surface *_outputSurface;
bool _continueParsing;
// Utility Functions
- void unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette);
+ void unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette);
void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *stream, byte bitsPerPixel, byte bytesPerPixel);
- void skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette);
+ void skipBitsRect(Common::SeekableReadStream &stream, bool withPalette);
void decodeCompressedQuickTime(Common::SeekableReadStream &stream);
void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel);
diff --git a/graphics/decoders/png.cpp b/graphics/decoders/png.cpp
index b87b6fdc7a..4f917b44b1 100644
--- a/graphics/decoders/png.cpp
+++ b/graphics/decoders/png.cpp
@@ -20,86 +20,25 @@
*
*/
+// Since we need to work with libpng here, we need to allow all symbols
+// to avoid compilation issues.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/scummsys.h"
+
+#ifdef USE_PNG
+#include <png.h>
+#endif
+
#include "graphics/decoders/png.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
-#include "common/endian.h"
-#include "common/memstream.h"
#include "common/stream.h"
-#include "common/types.h"
-#include "common/util.h"
-#include "common/zlib.h"
-
-// PNG decoder, based on the W3C specs:
-// http://www.w3.org/TR/PNG/
-// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
-// http://members.gamedev.net/lode/projects/LodePNG/
-
-/*
-LodePNG version 20101211
-
-Copyright (c) 2005-2010 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-*/
namespace Graphics {
-enum PNGChunks {
- // == Critical chunks =====================================================
- kChunkIHDR = MKTAG('I','H','D','R'), // Image header
- kChunkIDAT = MKTAG('I','D','A','T'), // Image data
- kChunkPLTE = MKTAG('P','L','T','E'), // Palette
- kChunkIEND = MKTAG('I','E','N','D'), // Image trailer
- // == Ancillary chunks ====================================================
- kChunktRNS = MKTAG('t','R','N','S') // Transparency
- // All of the other ancillary chunks are ignored. They're added here for
- // reference only.
- // cHRM - Primary chromacities and white point
- // gAMA - Image gamma
- // iCCP - Embedded ICC profile
- // sBIT - Significant bits
- // sRGB - Standard RGB color space
- // tEXT - Textual data
- // sTXt - Compressed textual data
- // iTXt - International textual data
- // bKGD - Background color
- // hIST - Image histogram
- // pHYs - Physical pixel dimensions
- // sPLT - Suggested palette
- // tIME - Image last-modification time
-};
-
-// Refer to http://www.w3.org/TR/PNG/#9Filters
-enum PNGFilters {
- kFilterNone = 0,
- kFilterSub = 1,
- kFilterUp = 2,
- kFilterAverage = 3,
- kFilterPaeth = 4
-};
-
-PNGDecoder::PNGDecoder() : _compressedBuffer(0), _compressedBufferSize(0),
- _transparentColorSpecified(false), _outputSurface(0) {
+PNGDecoder::PNGDecoder() : _outputSurface(0), _palette(0), _paletteColorCount(0) {
}
PNGDecoder::~PNGDecoder() {
@@ -112,12 +51,41 @@ void PNGDecoder::destroy() {
delete _outputSurface;
_outputSurface = 0;
}
+ delete[] _palette;
+ _palette = NULL;
}
+#ifdef USE_PNG
+// libpng-error-handling:
+void pngError(png_structp pngptr, png_const_charp errorMsg) {
+ error("%s", errorMsg);
+}
+
+void pngWarning(png_structp pngptr, png_const_charp warningMsg) {
+ warning("%s", warningMsg);
+}
+
+// libpng-I/O-helper:
+void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) {
+ void *readIOptr = png_get_io_ptr(pngPtr);
+ Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr;
+ stream->read(data, length);
+}
+#endif
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
+#ifdef USE_PNG
destroy();
- uint32 chunkLength = 0, chunkType = 0;
_stream = &stream;
// First, check the PNG signature
@@ -130,374 +98,144 @@ bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
return false;
}
- // Start reading chunks till we reach an IEND chunk
- while (chunkType != kChunkIEND) {
- // The chunk length does not include the type or CRC bytes
- chunkLength = _stream->readUint32BE();
- chunkType = _stream->readUint32BE();
-
- switch (chunkType) {
- case kChunkIHDR:
- readHeaderChunk();
- break;
- case kChunkIDAT:
- if (_compressedBufferSize == 0) {
- _compressedBufferSize += chunkLength;
- _compressedBuffer = (byte *)malloc(_compressedBufferSize);
- _stream->read(_compressedBuffer, chunkLength);
- } else {
- // Expand the buffer
- uint32 prevSize = _compressedBufferSize;
- _compressedBufferSize += chunkLength;
- byte *tmp = new byte[prevSize];
- memcpy(tmp, _compressedBuffer, prevSize);
- free(_compressedBuffer);
- _compressedBuffer = (byte *)malloc(_compressedBufferSize);
- memcpy(_compressedBuffer, tmp, prevSize);
- delete[] tmp;
- _stream->read(_compressedBuffer + prevSize, chunkLength);
- }
- break;
- case kChunkPLTE: // only available in indexed PNGs
- if (_header.colorType != kIndexed)
- error("A palette chunk has been found in a non-indexed PNG file");
- if (chunkLength % 3 != 0)
- error("Palette chunk not divisible by 3");
-
- _paletteEntries = chunkLength / 3;
- _stream->read(_palette, _paletteEntries * 3);
- memset(_paletteTransparency, 0xff, sizeof(_paletteTransparency));
- break;
- case kChunkIEND:
- // End of stream
- break;
- case kChunktRNS:
- readTransparencyChunk(chunkLength);
- break;
- default:
- // Skip the chunk content
- _stream->skip(chunkLength);
- break;
- }
-
- if (chunkType != kChunkIEND)
- _stream->skip(4); // skip the chunk CRC checksum
+ // The following is based on the guide provided in:
+ //http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3
+ //http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf
+ // along with the png-loading code used in the sword25-engine.
+ png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!pngPtr) {
+ delete _stream;
+ return false;
+ }
+ png_infop infoPtr = png_create_info_struct(pngPtr);
+ if (!infoPtr) {
+ png_destroy_read_struct(&pngPtr, NULL, NULL);
+ delete _stream;
+ return false;
+ }
+ png_infop endInfo = png_create_info_struct(pngPtr);
+ if (!endInfo) {
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+ delete _stream;
+ return false;
}
- // We no longer need the file stream, thus close it here
- _stream = 0;
+ png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
+ // TODO: The manual says errors should be handled via setjmp
- // Unpack the compressed buffer
- Common::MemoryReadStream *compData = new Common::MemoryReadStream(_compressedBuffer, _compressedBufferSize, DisposeAfterUse::YES);
- _imageData = Common::wrapCompressedReadStream(compData);
+ png_set_read_fn(pngPtr, _stream, pngReadFromStream);
+ png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE);
+ // We already verified the PNG-header
+ png_set_sig_bytes(pngPtr, 8);
- // Construct the final image
- constructImage();
+ // Read PNG header
+ png_read_info(pngPtr, infoPtr);
- // Close the uncompressed stream, which will also delete the memory stream,
- // and thus the original compressed buffer
- delete _imageData;
+ // No handling for unknown chunks yet.
+ int bitDepth, colorType, width, height, interlaceType;
+ png_uint_32 w, h;
+ png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL);
+ width = w;
+ height = h;
- return true;
-}
-
-/**
- * Paeth predictor, used by PNG filter type 4
- * The parameters are of signed 16-bit integers, but should come
- * from unsigned chars. The integers are only needed to make
- * the paeth calculation correct.
- *
- * Taken from lodePNG, with a slight patch:
- * http://www.atalasoft.com/cs/blogs/stevehawley/archive/2010/02/23/libpng-you-re-doing-it-wrong.aspx
- */
-byte PNGDecoder::paethPredictor(int16 a, int16 b, int16 c) {
- int16 pa = ABS<int16>(b - c);
- int16 pb = ABS<int16>(a - c);
- int16 pc = ABS<int16>(a + b - c - c);
-
- if (pa <= MIN<int16>(pb, pc))
- return (byte)a;
- else if (pb <= pc)
- return (byte)b;
- else
- return (byte)c;
-}
+ // Allocate memory for the final image data.
+ // To keep memory framentation low this happens before allocating memory for temporary image data.
+ _outputSurface = new Graphics::Surface();
-/**
- * Unfilters a filtered PNG scan line.
- * PNG filters are defined in: http://www.w3.org/TR/PNG/#9Filters
- * Note that filters are always applied to bytes
- *
- * Taken from lodePNG
- */
-void PNGDecoder::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) {
- uint16 i;
-
- switch (filterType) {
- case kFilterNone: // no change
- for (i = 0; i < length; i++)
- dest[i] = scanLine[i];
- break;
- case kFilterSub: // add the bytes to the left
- for (i = 0; i < byteWidth; i++)
- dest[i] = scanLine[i];
- for (i = byteWidth; i < length; i++)
- dest[i] = scanLine[i] + dest[i - byteWidth];
- break;
- case kFilterUp: // add the bytes of the above scanline
- if (prevLine) {
- for (i = 0; i < length; i++)
- dest[i] = scanLine[i] + prevLine[i];
- } else {
- for (i = 0; i < length; i++)
- dest[i] = scanLine[i];
+ // Images of all color formats except PNG_COLOR_TYPE_PALETTE
+ // will be transformed into ARGB images
+ if (colorType == PNG_COLOR_TYPE_PALETTE && !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
+ int numPalette = 0;
+ png_colorp palette = NULL;
+ uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette);
+ if (success != PNG_INFO_PLTE) {
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+ return false;
}
- break;
- case kFilterAverage: // average value of the left and top left
- if (prevLine) {
- for (i = 0; i < byteWidth; i++)
- dest[i] = scanLine[i] + prevLine[i] / 2;
- for (i = byteWidth; i < length; i++)
- dest[i] = scanLine[i] + ((dest[i - byteWidth] + prevLine[i]) / 2);
- } else {
- for (i = 0; i < byteWidth; i++)
- dest[i] = scanLine[i];
- for (i = byteWidth; i < length; i++)
- dest[i] = scanLine[i] + dest[i - byteWidth] / 2;
+ _paletteColorCount = numPalette;
+ _palette = new byte[_paletteColorCount * 3];
+ for (int i = 0; i < _paletteColorCount; i++) {
+ _palette[(i * 3)] = palette[i].red;
+ _palette[(i * 3) + 1] = palette[i].green;
+ _palette[(i * 3) + 2] = palette[i].blue;
+
}
- break;
- case kFilterPaeth: // Paeth filter: http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth
- if (prevLine) {
- for(i = 0; i < byteWidth; i++)
- dest[i] = (scanLine[i] + prevLine[i]); // paethPredictor(0, prevLine[i], 0) is always prevLine[i]
- for(i = byteWidth; i < length; i++)
- dest[i] = (scanLine[i] + paethPredictor(dest[i - byteWidth], prevLine[i], prevLine[i - byteWidth]));
- } else {
- for(i = 0; i < byteWidth; i++)
- dest[i] = scanLine[i];
- for(i = byteWidth; i < length; i++)
- dest[i] = (scanLine[i] + dest[i - byteWidth]); // paethPredictor(dest[i - byteWidth], 0, 0) is always dest[i - byteWidth]
+ _outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ png_set_packing(pngPtr);
+ } else {
+ _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+ if (!_outputSurface->pixels) {
+ error("Could not allocate memory for output image.");
}
- break;
- default:
- error("Unknown line filter");
- }
+ if (bitDepth == 16)
+ png_set_strip_16(pngPtr);
+ if (bitDepth < 8)
+ png_set_expand(pngPtr);
+ if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS))
+ png_set_expand(pngPtr);
+ if (colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(pngPtr);
+
+ // PNGs are Big-Endian:
+#ifdef SCUMM_LITTLE_ENDIAN
+ png_set_bgr(pngPtr);
+ png_set_swap_alpha(pngPtr);
+ if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_filler(pngPtr, 0xff, PNG_FILLER_BEFORE);
+#else
+ if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER);
+#endif
-}
+ }
-int PNGDecoder::getBytesPerPixel() const {
- return (getNumColorChannels() * _header.bitDepth + 7) / 8;
-}
+ // After the transformations have been registered, the image data is read again.
+ png_set_interlace_handling(pngPtr);
+ png_read_update_info(pngPtr, infoPtr);
+ png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL);
+ width = w;
+ height = h;
+
+ if (interlaceType == PNG_INTERLACE_NONE) {
+ // PNGs without interlacing can simply be read row by row.
+ for (int i = 0; i < height; i++) {
+ png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL);
+ }
+ } else {
+ // PNGs with interlacing require us to allocate an auxillary
+ // buffer with pointers to all row starts.
-void PNGDecoder::constructImage() {
- assert (_header.bitDepth != 0);
-
- int bytesPerPixel = getBytesPerPixel();
- int pitch = bytesPerPixel * _header.width;
- byte *unfilteredSurface = new byte[pitch * _header.height];
- byte *dest = unfilteredSurface;
- uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8;
- byte *scanLine = new byte[scanLineWidth];
- byte *prevLine = 0;
-
- switch(_header.interlaceType) {
- case kNonInterlaced:
- for (uint16 y = 0; y < _header.height; y++) {
- byte filterType = _imageData->readByte();
- _imageData->read(scanLine, scanLineWidth);
- unfilterScanLine(dest, scanLine, prevLine, bytesPerPixel, filterType, scanLineWidth);
- prevLine = dest;
- dest += pitch;
+ // Allocate row pointer buffer
+ png_bytep *rowPtr = new png_bytep[height];
+ if (!rowPtr) {
+ error("Could not allocate memory for row pointers.");
}
- break;
- case kInterlaced:
- // Theoretically, this shouldn't be needed, as interlacing is only
- // useful for web images. Interlaced PNG images require more complex
- // handling, so unless having support for such images is needed, there
- // is no reason to add support for them.
- error("TODO: Support for interlaced PNG images");
- break;
- }
- delete[] scanLine;
+ // Initialize row pointers
+ for (int i = 0; i < height; i++)
+ rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i);
- constructOutput(unfilteredSurface);
- delete[] unfilteredSurface;
-}
+ // Read image data
+ png_read_image(pngPtr, rowPtr);
-Graphics::PixelFormat PNGDecoder::findPixelFormat() const {
- // Try to find the best pixel format based on what we have here
- // Which is basically 8bpp for paletted non-transparent
- // and 32bpp for everything else
-
- switch (_header.colorType) {
- case kIndexed:
- if (!_transparentColorSpecified)
- return Graphics::PixelFormat::createFormatCLUT8();
- // fall through
- case kGrayScale:
- case kTrueColor:
- case kGrayScaleWithAlpha:
- case kTrueColorWithAlpha:
- // We'll go with standard RGBA 32-bit
- return Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+ // Free row pointer buffer
+ delete[] rowPtr;
}
- error("Unknown PNG color type");
- return Graphics::PixelFormat();
-}
+ // Read additional data at the end.
+ png_read_end(pngPtr, NULL);
-void PNGDecoder::constructOutput(const byte *surface) {
- _outputSurface = new Graphics::Surface();
- _outputSurface->create(_header.width, _header.height, findPixelFormat());
-
- const byte *src = surface;
- byte a = 0xFF;
- int bytesPerPixel = getBytesPerPixel();
-
- if (_header.colorType != kIndexed) {
- if (_header.colorType == kTrueColor ||
- _header.colorType == kTrueColorWithAlpha) {
- if (bytesPerPixel != 3 && bytesPerPixel != 4)
- error("Unsupported truecolor PNG format");
- } else if (_header.colorType == kGrayScale ||
- _header.colorType == kGrayScaleWithAlpha) {
- if (bytesPerPixel != 1 && bytesPerPixel != 2)
- error("Unsupported grayscale PNG format");
- }
-
- for (uint16 i = 0; i < _outputSurface->h; i++) {
- for (uint16 j = 0; j < _outputSurface->w; j++) {
- uint32 result = 0;
-
- switch (bytesPerPixel) {
- case 1: // Grayscale
- if (_transparentColorSpecified)
- a = (src[0] == _transparentColor[0]) ? 0 : 0xFF;
- result = _outputSurface->format.ARGBToColor(a, src[0], src[0], src[0]);
- break;
- case 2: // Grayscale + alpha
- result = _outputSurface->format.ARGBToColor(src[1], src[0], src[0], src[0]);
- break;
- case 3: // RGB
- if (_transparentColorSpecified) {
- bool isTransparentColor = (src[0] == _transparentColor[0] &&
- src[1] == _transparentColor[1] &&
- src[2] == _transparentColor[2]);
- a = isTransparentColor ? 0 : 0xFF;
- }
-
- result = _outputSurface->format.ARGBToColor(a, src[0], src[1], src[2]);
- break;
- case 4: // RGBA
- result = _outputSurface->format.ARGBToColor(src[3], src[0], src[1], src[2]);
- break;
- }
-
- *((uint32 *)_outputSurface->getBasePtr(j, i)) = result;
- src += bytesPerPixel;
- }
- }
- } else {
- uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth);
-
- // Convert the indexed surface to the target pixel format
- for (uint16 i = 0; i < _outputSurface->h; i++) {
- int data = 0;
- int bitCount = 8;
- const byte *src1 = src;
-
- for (uint16 j = 0; j < _outputSurface->w; j++) {
- if (bitCount == 8) {
- data = *src;
- src++;
- }
-
- byte index = (data & mask) >> (8 - _header.bitDepth);
- data = (data << _header.bitDepth) & 0xff;
- bitCount -= _header.bitDepth;
-
- if (bitCount == 0)
- bitCount = 8;
-
- if (_transparentColorSpecified) {
- byte r = _palette[index * 3 + 0];
- byte g = _palette[index * 3 + 1];
- byte b = _palette[index * 3 + 2];
- a = _paletteTransparency[index];
- *((uint32 *)_outputSurface->getBasePtr(j, i)) = _outputSurface->format.ARGBToColor(a, r, g, b);
- } else {
- *((byte *)_outputSurface->getBasePtr(j, i)) = index;
- }
- }
-
- src = src1 + _outputSurface->w;
- }
- }
-}
+ // Destroy libpng structures
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
-void PNGDecoder::readHeaderChunk() {
- _header.width = _stream->readUint32BE();
- _header.height = _stream->readUint32BE();
- _header.bitDepth = _stream->readByte();
- if (_header.bitDepth > 8)
- error("Only PNGs with a bit depth of 1-8 bits are supported (i.e. PNG24)");
- _header.colorType = (PNGColorType)_stream->readByte();
- _header.compressionMethod = _stream->readByte();
- // Compression methods: http://www.w3.org/TR/PNG/#10Compression
- // Only compression method 0 (deflate) is documented and supported
- if (_header.compressionMethod != 0)
- error("Unknown PNG compression method: %d", _header.compressionMethod);
- _header.filterMethod = _stream->readByte();
- // Filter methods: http://www.w3.org/TR/PNG/#9Filters
- // Only filter method 0 is documented and supported
- if (_header.filterMethod != 0)
- error("Unknown PNG filter method: %d", _header.filterMethod);
- _header.interlaceType = (PNGInterlaceType)_stream->readByte();
-}
-
-byte PNGDecoder::getNumColorChannels() const {
- switch (_header.colorType) {
- case kGrayScale:
- return 1; // Gray
- case kTrueColor:
- return 3; // RGB
- case kIndexed:
- return 1; // Indexed
- case kGrayScaleWithAlpha:
- return 2; // Gray + Alpha
- case kTrueColorWithAlpha:
- return 4; // RGBA
- default:
- error("Unknown color type");
- }
-}
+ // We no longer need the file stream, thus close it here
+ _stream = 0;
-void PNGDecoder::readTransparencyChunk(uint32 chunkLength) {
- _transparentColorSpecified = true;
-
- switch(_header.colorType) {
- case kGrayScale:
- _transparentColor[0] = _stream->readUint16BE();
- _transparentColor[1] = _transparentColor[0];
- _transparentColor[2] = _transparentColor[0];
- break;
- case kTrueColor:
- _transparentColor[0] = _stream->readUint16BE();
- _transparentColor[1] = _stream->readUint16BE();
- _transparentColor[2] = _stream->readUint16BE();
- break;
- case kIndexed:
- _stream->read(_paletteTransparency, chunkLength);
-
- // A transparency chunk may have less entries
- // than the palette entries. The remaining ones
- // are unmodified (set to 255). Check here:
- // http://www.w3.org/TR/PNG/#11tRNS
- break;
- default:
- error("Transparency chunk found in a PNG that has a separate transparency channel");
- }
+ return true;
+#else
+ return false;
+#endif
}
} // End of Graphics namespace
diff --git a/graphics/decoders/png.h b/graphics/decoders/png.h
index 1da0bea1ab..e52ddabd7d 100644
--- a/graphics/decoders/png.h
+++ b/graphics/decoders/png.h
@@ -24,33 +24,12 @@
* PNG decoder used in engines:
* - sword25
* Dependencies:
- * - zlib
+ * - libpng
*/
#ifndef GRAPHICS_PNG_H
#define GRAPHICS_PNG_H
-// PNG decoder, based on the W3C specs:
-// http://www.w3.org/TR/PNG/
-// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
-// http://members.gamedev.net/lode/projects/LodePNG/
-
-// All the numbers are BE: http://www.w3.org/TR/PNG/#7Integers-and-byte-order
-
-// Note: At the moment, this decoder only supports non-interlaced images, and
-// does not support truecolor/grayscale images with 16bit depth.
-//
-// Theoretically, interlaced images shouldn't be needed for games, as
-// interlacing is only useful for images in websites.
-//
-// PNG images with 16bit depth (i.e. 48bit images) are quite rare, and can
-// theoretically contain more than 16.7 millions of colors (the so-called "deep
-// color" representation). In essence, each of the R, G, B and A components in
-// them is specified with 2 bytes, instead of 1. However, the PNG specification
-// always refers to color components with 1 byte each, so this part of the spec
-// is a bit unclear. For now, these won't be supported, until a suitable sample
-// is found.
-
#include "common/scummsys.h"
#include "common/textconsole.h"
#include "graphics/decoders/image_decoder.h"
@@ -73,61 +52,13 @@ public:
void destroy();
const Graphics::Surface *getSurface() const { return _outputSurface; }
const byte *getPalette() const { return _palette; }
-
+ uint16 getPaletteColorCount() const { return _paletteColorCount; }
private:
- enum PNGColorType {
- kGrayScale = 0, // bit depths: 1, 2, 4, 8, 16
- kTrueColor = 2, // bit depths: 8, 16
- kIndexed = 3, // bit depths: 1, 2, 4, 8
- kGrayScaleWithAlpha = 4, // bit depths: 8, 16
- kTrueColorWithAlpha = 6 // bit depths: 8, 16
- };
-
- enum PNGInterlaceType {
- kNonInterlaced = 0,
- kInterlaced = 1
- };
-
- struct PNGHeader {
- uint32 width;
- uint32 height;
- byte bitDepth;
- PNGColorType colorType;
- byte compressionMethod;
- byte filterMethod;
- PNGInterlaceType interlaceType;
- };
-
- void readHeaderChunk();
- byte getNumColorChannels() const;
-
- void readPaletteChunk();
- void readTransparencyChunk(uint32 chunkLength);
-
- void constructImage();
- void unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length);
- byte paethPredictor(int16 a, int16 b, int16 c);
-
- // The original file stream
Common::SeekableReadStream *_stream;
- // The unzipped image data stream
- Common::SeekableReadStream *_imageData;
-
- PNGHeader _header;
-
- byte _palette[256 * 3]; // RGB
- byte _paletteTransparency[256];
- uint16 _paletteEntries;
- uint16 _transparentColor[3];
- bool _transparentColorSpecified;
-
- byte *_compressedBuffer;
- uint32 _compressedBufferSize;
+ byte *_palette;
+ uint16 _paletteColorCount;
Graphics::Surface *_outputSurface;
- Graphics::PixelFormat findPixelFormat() const;
- int getBytesPerPixel() const;
- void constructOutput(const byte *surface);
};
} // End of namespace Graphics
diff --git a/graphics/decoders/tga.cpp b/graphics/decoders/tga.cpp
new file mode 100644
index 0000000000..c3b9d84055
--- /dev/null
+++ b/graphics/decoders/tga.cpp
@@ -0,0 +1,427 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Based on code from xoreos https://github.com/DrMcCoy/xoreos/
+ * relicensed under GPLv2+ with permission from DrMcCoy and clone2727
+ */
+
+#include "common/util.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+#include "common/error.h"
+
+#include "graphics/decoders/tga.h"
+
+namespace Graphics {
+
+TGADecoder::TGADecoder() {
+ _colorMapSize = 0;
+ _colorMapOrigin = 0;
+ _colorMapLength = 0;
+ _colorMapEntryLength = 0;
+ _colorMap = NULL;
+}
+
+TGADecoder::~TGADecoder() {
+ destroy();
+}
+
+void TGADecoder::destroy() {
+ _surface.free();
+ delete[] _colorMap;
+}
+
+bool TGADecoder::loadStream(Common::SeekableReadStream &tga) {
+ byte imageType, pixelDepth;
+ bool success;
+ success = readHeader(tga, imageType, pixelDepth);
+ if (success) {
+ switch (imageType) {
+ case TYPE_BW:
+ case TYPE_TRUECOLOR:
+ success = readData(tga, imageType, pixelDepth);
+ break;
+ case TYPE_RLE_BW:
+ case TYPE_RLE_TRUECOLOR:
+ case TYPE_RLE_CMAP:
+ success = readDataRLE(tga, imageType, pixelDepth);
+ break;
+ case TYPE_CMAP:
+ success = readDataColorMapped(tga, imageType, pixelDepth);
+ break;
+ default:
+ success = false;
+ break;
+ }
+ }
+ if (tga.err() || !success) {
+ warning("Failed reading TGA-file");
+ return false;
+ }
+ return success;
+}
+
+bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) {
+ if (!tga.seek(0)) {
+ warning("Failed reading TGA-file");
+ return false;
+ }
+
+ // TGAs have an optional "id" string in the header
+ uint32 idLength = tga.readByte();
+
+ // Number of colors in the color map / palette
+ int hasColorMap = tga.readByte();
+
+ // Image type. See header for numeric constants
+ imageType = tga.readByte();
+
+ switch (imageType) {
+ case TYPE_CMAP:
+ case TYPE_TRUECOLOR:
+ case TYPE_BW:
+ case TYPE_RLE_CMAP:
+ case TYPE_RLE_TRUECOLOR:
+ case TYPE_RLE_BW:
+ break;
+ default:
+ warning("Unsupported image type: %d", imageType);
+ return false;
+ }
+
+ // Color map specifications
+ if (hasColorMap == 0) {
+ tga.skip(5);
+ } else {
+ _colorMapOrigin = tga.readUint16LE();
+ _colorMapLength = tga.readUint16LE();
+ _colorMapEntryLength = tga.readByte();
+ }
+ // Origin-defintions
+ tga.skip(2 + 2);
+
+ // Image dimensions
+ _surface.w = tga.readUint16LE();
+ _surface.h = tga.readUint16LE();
+
+ // Bits per pixel
+ pixelDepth = tga.readByte();
+ _surface.format.bytesPerPixel = pixelDepth / 8;
+
+ // Image descriptor
+ byte imgDesc = tga.readByte();
+ int attributeBits = imgDesc & 0x0F;
+ assert((imgDesc & 0x10) == 0);
+ _originTop = (imgDesc & 0x20);
+
+ // Interleaving is not handled at this point
+ //int interleave = (imgDesc & 0xC);
+ if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) {
+ if (pixelDepth == 8) {
+ _format = PixelFormat::createFormatCLUT8();
+ } else {
+ warning("Unsupported index-depth: %d", pixelDepth);
+ return false;
+ }
+ } else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) {
+ if (pixelDepth == 24) {
+ _format = PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
+ } else if (pixelDepth == 32) {
+ // HACK: According to the spec, attributeBits should determine the amount
+ // of alpha-bits, however, as the game files that use this decoder seems
+ // to ignore that fact, we force the amount to 8 for 32bpp files for now.
+ _format = PixelFormat(4, 8, 8, 8, /* attributeBits */ 8, 16, 8, 0, 24);
+ } else if (pixelDepth == 16 && imageType == TYPE_TRUECOLOR) {
+ // 16bpp TGA is ARGB1555
+ _format = PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15);
+ } else {
+ warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
+ return false;
+ }
+ } else if (imageType == TYPE_BW || TYPE_RLE_BW) {
+ if (pixelDepth == 8) {
+ _format = PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0);
+ } else {
+ warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth);
+ return false;
+ }
+
+ } else {
+ warning("Unsupported image type: %d", imageType);
+ return false;
+ }
+
+ // Skip the id string
+ tga.skip(idLength);
+
+ if (hasColorMap) {
+ return readColorMap(tga, imageType, pixelDepth);
+ }
+ return true;
+}
+
+bool TGADecoder::readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
+ _colorMap = new byte[3 * _colorMapLength];
+ for (int i = 0; i < _colorMapLength * 3; i += 3) {
+ byte r, g, b;
+ if (_colorMapEntryLength == 32) {
+ byte a;
+ PixelFormat format(4, 8, 8, 8, 0, 16, 8, 0, 24);
+ uint32 color = tga.readUint32LE();
+ format.colorToARGB(color, a, r, g, b);
+ } else if (_colorMapEntryLength == 24) {
+ r = tga.readByte();
+ g = tga.readByte();
+ b = tga.readByte();
+ } else if (_colorMapEntryLength == 16) {
+ byte a;
+ PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 15);
+ uint16 color = tga.readUint16LE();
+ format.colorToARGB(color, a, r, g, b);
+ } else {
+ warning("Unsupported image type: %d", imageType);
+ r = g = b = 0;
+ }
+#ifdef SCUMM_LITTLE_ENDIAN
+ _colorMap[i] = r;
+ _colorMap[i + 1] = g;
+ _colorMap[i + 2] = b;
+#else
+ _colorMap[i] = b;
+ _colorMap[i + 1] = g;
+ _colorMap[i + 2] = r;
+#endif
+ }
+ return true;
+}
+
+// Additional information found from http://paulbourke.net/dataformats/tga/
+// With some details from the link referenced in the header.
+bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
+ // TrueColor
+ if (imageType == TYPE_TRUECOLOR) {
+ _surface.create(_surface.w, _surface.h, _format);
+
+ if (pixelDepth == 16) {
+ for (int i = 0; i < _surface.h; i++) {
+ uint16 *dst;
+ if (!_originTop) {
+ dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (uint16 *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ *dst++ = tga.readUint16LE();
+ }
+ }
+ } else if (pixelDepth == 32) {
+ for (int i = 0; i < _surface.h; i++) {
+ uint32 *dst;
+ if (!_originTop) {
+ dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (uint32 *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ *dst++ = tga.readUint32LE();
+ }
+ }
+ } else if (pixelDepth == 24) {
+ for (int i = 0; i < _surface.h; i++) {
+ byte *dst;
+ if (!_originTop) {
+ dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (byte *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ byte r = tga.readByte();
+ byte g = tga.readByte();
+ byte b = tga.readByte();
+#ifdef SCUMM_LITTLE_ENDIAN
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+#else
+ *dst++ = b;
+ *dst++ = g;
+ *dst++ = r;
+#endif
+ }
+ }
+ }
+ // Black/White
+ } else if (imageType == TYPE_BW) {
+ _surface.create(_surface.w, _surface.h, _format);
+
+ byte *data = (byte *)_surface.pixels;
+ uint32 count = _surface.w * _surface.h;
+
+ while (count-- > 0) {
+ byte g = tga.readByte();
+ *data++ = g;
+ *data++ = g;
+ *data++ = g;
+ *data++ = g;
+ }
+ }
+ return true;
+}
+
+bool TGADecoder::readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth) {
+ // Color-mapped
+ if (imageType == TYPE_CMAP) {
+ _surface.create(_surface.w, _surface.h, _format);
+ if (indexDepth == 8) {
+ for (int i = 0; i < _surface.h; i++) {
+ byte *dst;
+ if (!_originTop) {
+ dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1);
+ } else {
+ dst = (byte *)_surface.getBasePtr(0, i);
+ }
+ for (int j = 0; j < _surface.w; j++) {
+ byte index = tga.readByte();
+ *dst++ = index;
+ }
+ }
+ } else if (indexDepth == 16) {
+ warning("16 bit indexes not supported");
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) {
+ // RLE-TrueColor / RLE-Black/White
+ if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) {
+ _surface.create(_surface.w, _surface.h, _format);
+ uint32 count = _surface.w * _surface.h;
+ byte *data = (byte *)_surface.pixels;
+
+ while (count > 0) {
+ uint32 header = tga.readByte();
+ byte type = (header & 0x80) >> 7;
+ uint32 rleCount = (header & 0x7F) + 1;
+
+ // RLE-packet
+ if (type == 1) {
+ if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
+ uint32 color = tga.readUint32LE();
+ while (rleCount-- > 0) {
+ *((uint32 *)data) = color;
+ data += 4;
+ count--;
+ }
+ } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
+ byte r = tga.readByte();
+ byte g = tga.readByte();
+ byte b = tga.readByte();
+ while (rleCount-- > 0) {
+#ifdef SCUMM_LITTLE_ENDIAN
+ *data++ = r;
+ *data++ = g;
+ *data++ = b;
+#else
+ *data++ = b;
+ *data++ = g;
+ *data++ = r;
+#endif
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
+ byte color = tga.readByte();
+ while (rleCount-- > 0) {
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
+ byte index = tga.readByte();
+ while (rleCount-- > 0) {
+ *data++ = index;
+ count--;
+ }
+ } else {
+ warning("Unhandled pixel-depth for image-type 10");
+ return false;
+ }
+ // Raw-packet
+ } else if (type == 0) {
+ if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) {
+ while (rleCount-- > 0) {
+ uint32 color = tga.readUint32LE();
+ *((uint32 *)data) = color;
+ data += 4;
+ count--;
+ }
+ } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) {
+ while (rleCount-- > 0) {
+ byte r = tga.readByte();
+ byte g = tga.readByte();
+ byte b = tga.readByte();
+#ifdef SCUMM_LITTLE_ENDIAN
+ *data++ = r;
+ *data++ = g;
+ *data++ = b;
+#else
+ *data++ = b;
+ *data++ = g;
+ *data++ = r;
+#endif
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) {
+ while (rleCount-- > 0) {
+ byte color = tga.readByte();
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ *data++ = color;
+ count--;
+ }
+ } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) {
+ while (rleCount-- > 0) {
+ byte index = tga.readByte();
+ *data++ = index;
+ count--;
+ }
+ } else {
+ warning("Unhandled pixel-depth for image-type 10");
+ return false;
+ }
+ } else {
+ warning("Unknown header for RLE-packet %d", type);
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/decoders/tga.h b/graphics/decoders/tga.h
new file mode 100644
index 0000000000..dfdc5a4da9
--- /dev/null
+++ b/graphics/decoders/tga.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Based on code from eos https://github.com/DrMcCoy/xoreos/
+ * relicensed under GPLv2+ with permission from DrMcCoy and clone2727
+ */
+
+/*
+ * TGA decoder used in engines:
+ * - none
+ */
+
+#ifndef GRAPHICS_DECODERS_TGA_H
+#define GRAPHICS_DECODERS_TGA_H
+
+#include "graphics/surface.h"
+#include "graphics/decoders/image_decoder.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+/** TarGa image-decoder
+ * The following variations of TGA are supported:
+ * - Type 1 - Color-mapped images in 16/24/32 bpp with 8 bit indexes
+ * - Type 2 - 16/24/32 bpp Top AND Bottom origined.
+ * - Type 3 - Black/White images, 8bpp.
+ * - Type 9 - RLE-encoded color-mapped images. (8 bit indexes only)
+ * - Type 10 - RLE-encoded TrueColor, 24/32bpp.
+ * - Type 11 - RLE-encoded Black/White, 8bpp.
+ *
+ * No images are returned with a palette, instead they are converted
+ * to 16 bpp for Type 1, or 32 bpp for Black/White-images.
+ */
+class TGADecoder : public ImageDecoder {
+public:
+ TGADecoder();
+ virtual ~TGADecoder();
+ virtual void destroy();
+ virtual const Surface *getSurface() const { return &_surface; }
+ virtual const byte *getPalette() const { return _colorMap; }
+ virtual uint16 getPaletteColorCount() const { return _colorMapLength; }
+ virtual bool loadStream(Common::SeekableReadStream &stream);
+private:
+ // Format-spec from:
+ //http://www.ludorg.net/amnesia/TGA_File_Format_Spec.html
+ enum {
+ TYPE_CMAP = 1,
+ TYPE_TRUECOLOR = 2,
+ TYPE_BW = 3,
+ TYPE_RLE_CMAP = 9,
+ TYPE_RLE_TRUECOLOR = 10,
+ TYPE_RLE_BW = 11
+ };
+
+ // Color-map:
+ bool _colorMapSize;
+ byte *_colorMap;
+ int16 _colorMapOrigin;
+ int16 _colorMapLength;
+ byte _colorMapEntryLength;
+
+ // Origin may be at the top, or bottom
+ bool _originTop;
+
+ PixelFormat _format;
+ Surface _surface;
+ // Loading helpers
+ bool readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth);
+ bool readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
+ bool readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth);
+ bool readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
+ bool readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth);
+};
+
+} // End of namespace Graphics
+
+#endif // GRAPHICS_DECODERS_TGA_H
diff --git a/graphics/fontman.cpp b/graphics/fontman.cpp
index 8d967d595f..99dd3d664f 100644
--- a/graphics/fontman.cpp
+++ b/graphics/fontman.cpp
@@ -47,6 +47,13 @@ FontManager::FontManager() {
}
FontManager::~FontManager() {
+ for (uint i = 0; i < _ownedFonts.size(); ++i) {
+ const Font *font = _ownedFonts[i];
+ if (font == g_sysfont || font == g_sysfont_big || font == g_consolefont)
+ continue;
+ delete font;
+ }
+
delete g_sysfont;
g_sysfont = 0;
delete g_sysfont_big;
@@ -90,6 +97,8 @@ bool FontManager::assignFontToName(const Common::String &name, const Font *font)
Common::String lowercaseName = name;
lowercaseName.toLowercase();
_fontMap[lowercaseName] = font;
+ if (Common::find(_ownedFonts.begin(), _ownedFonts.end(), font) == _ownedFonts.end())
+ _ownedFonts.push_back(font);
return true;
}
@@ -116,8 +125,35 @@ bool FontManager::setFont(FontUsage usage, const BdfFont *font) {
void FontManager::removeFontName(const Common::String &name) {
Common::String lowercaseName = name;
lowercaseName.toLowercase();
+ if (!_fontMap.contains(lowercaseName))
+ return;
+
+ const Font *font = _fontMap[lowercaseName];
_fontMap.erase(lowercaseName);
+ // Check if we still have a copy of this font in the map.
+ bool stillHasFont = false;
+ for (Common::HashMap<Common::String, const Font *>::iterator i = _fontMap.begin(); i != _fontMap.end(); ++i) {
+ if (i->_value != font)
+ continue;
+ stillHasFont = true;
+ break;
+ }
+
+ if (!stillHasFont) {
+ // We don't have a copy of the font, so remove it from our list and delete it.
+ stillHasFont = true;
+ for (uint i = 0; i < _ownedFonts.size(); ++i) {
+ if (_ownedFonts[i] != font)
+ continue;
+ stillHasFont = false;
+ _ownedFonts.remove_at(i);
+ break;
+ }
+ assert(!stillHasFont);
+ delete font;
+ }
+
// In case the current localized font is removed, we fall back to the
// default font again.
if (_localizedFontName == lowercaseName)
diff --git a/graphics/fontman.h b/graphics/fontman.h
index 42f7d856fa..b06ddea860 100644
--- a/graphics/fontman.h
+++ b/graphics/fontman.h
@@ -60,7 +60,9 @@ public:
const Font *getFontByName(const Common::String &name) const;
/**
- * Associates a font object with an 'name'
+ * Associates a font object with an 'name'.
+ * The FontManager takes ownership of the provided font object
+ * and will delete it when necesssary.
*
* @param name the name of the font
* @param font the font object
@@ -111,6 +113,7 @@ private:
~FontManager();
Common::HashMap<Common::String, const Font *> _fontMap;
+ Common::Array<const Font *> _ownedFonts;
Common::String _localizedFontName;
};
diff --git a/graphics/fonts/bdf.h b/graphics/fonts/bdf.h
index 5b615cc043..b0166a2095 100644
--- a/graphics/fonts/bdf.h
+++ b/graphics/fonts/bdf.h
@@ -77,7 +77,7 @@ private:
#define DEFINE_FONT(n) \
const BdfFont *n = 0; \
void create_##n() { \
- n = new BdfFont(desc, DisposeAfterUse::YES); \
+ n = new BdfFont(desc, DisposeAfterUse::NO); \
}
#define FORWARD_DECLARE_FONT(n) \
diff --git a/graphics/fonts/consolefont.cpp b/graphics/fonts/consolefont.cpp
index 8244d75fc2..748aa08a5c 100644
--- a/graphics/fonts/consolefont.cpp
+++ b/graphics/fonts/consolefont.cpp
@@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:32:21 2012
#include "graphics/fonts/bdf.h"
-// Font information:
+// Font information:
// Name: -Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-1
// Size: 5x8
// Box: 5 8 0 -1
diff --git a/graphics/fonts/newfont.cpp b/graphics/fonts/newfont.cpp
index 10af1efb0c..4922e24676 100644
--- a/graphics/fonts/newfont.cpp
+++ b/graphics/fonts/newfont.cpp
@@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:33:07 2012
#include "graphics/fonts/bdf.h"
-// Font information:
+// Font information:
// Name: -Schumacher-Clean-Medium-R-Normal--12-120-75-75-C-60-ISO8859-1
// Size: 6x12
// Box: 6 12 0 -3
diff --git a/graphics/fonts/newfont_big.cpp b/graphics/fonts/newfont_big.cpp
index 0e61068ade..550d6dbfa9 100644
--- a/graphics/fonts/newfont_big.cpp
+++ b/graphics/fonts/newfont_big.cpp
@@ -1,7 +1,7 @@
// Generated by convbdf on Fri Jan 6 14:33:14 2012
#include "graphics/fonts/bdf.h"
-// Font information:
+// Font information:
// Name: -Adobe-Helvetica-Bold-R-Normal--12-120-75-75-P-70-ISO8859-1
// Size: 13x14
// Box: 13 15 -1 -3
diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp
index 7505f7913e..2b1dca1eae 100644
--- a/graphics/fonts/ttf.cpp
+++ b/graphics/fonts/ttf.cpp
@@ -43,10 +43,6 @@ namespace Graphics {
namespace {
-inline int ftFloor26_6(FT_Pos x) {
- return x / 64;
-}
-
inline int ftCeil26_6(FT_Pos x) {
return (x + 63) / 64;
}
@@ -70,6 +66,10 @@ private:
bool _initialized;
};
+void shutdownTTF() {
+ TTFLibrary::destroy();
+}
+
#define g_ttf ::Graphics::TTFLibrary::instance()
TTFLibrary::TTFLibrary() : _library(), _initialized(false) {
@@ -101,7 +101,7 @@ public:
TTFFont();
virtual ~TTFFont();
- bool load(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping);
+ bool load(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping);
virtual int getFontHeight() const;
@@ -157,7 +157,7 @@ TTFFont::~TTFFont() {
}
}
-bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping) {
+bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) {
if (!g_ttf.isInitialized())
return false;
@@ -195,7 +195,7 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome
// Check whether we have kerning support
_hasKerning = (FT_HAS_KERNING(_face) != 0);
- if (FT_Set_Char_Size(_face, 0, size * 64, 0, 0)) {
+ if (FT_Set_Char_Size(_face, 0, size * 64, dpi, dpi)) {
delete[] _ttfFile;
_ttfFile = 0;
@@ -462,10 +462,10 @@ bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) {
return true;
}
-Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping) {
+Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) {
TTFFont *font = new TTFFont();
- if (!font->load(stream, size, monochrome, mapping)) {
+ if (!font->load(stream, size, dpi, monochrome, mapping)) {
delete font;
return 0;
}
diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h
index 7222d6e112..e1464b1f45 100644
--- a/graphics/fonts/ttf.h
+++ b/graphics/fonts/ttf.h
@@ -32,7 +32,9 @@
namespace Graphics {
class Font;
-Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome = false, const uint32 *mapping = 0);
+Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi = 0, bool monochrome = false, const uint32 *mapping = 0);
+
+void shutdownTTF();
} // End of namespace Graphics
diff --git a/graphics/iff.cpp b/graphics/iff.cpp
index 7434a6bebc..4011126bd3 100644
--- a/graphics/iff.cpp
+++ b/graphics/iff.cpp
@@ -68,7 +68,7 @@ void ILBMDecoder::loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stre
Graphics::PackBitsReadStream packStream(*stream);
// setup a buffer to hold enough data to build a line in the output
- uint32 scanlineWidth = ((_header.width + 15)/16) << 1;
+ uint32 scanlineWidth = ((_header.width + 15) / 16) << 1;
byte *scanline = new byte[scanlineWidth * _header.depth];
for (uint i = 0; i < _header.height; ++i) {
@@ -82,7 +82,7 @@ void ILBMDecoder::loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stre
out += outPitch;
}
- delete []scanline;
+ delete[] scanline;
break;
}
@@ -121,15 +121,12 @@ void ILBMDecoder::planarToChunky(byte *out, uint32 outPitch, byte *in, uint32 in
// then output the pixel according to the requested packing
if (!packPlanes) {
out[x] = pix;
- } else
- if (nPlanes == 1) {
- out[x/8] |= (pix << (x & 7));
- } else
- if (nPlanes == 2) {
- out[x/4] |= (pix << ((x & 3) << 1));
- } else
- if (nPlanes == 4) {
- out[x/2] |= (pix << ((x & 1) << 2));
+ } else if (nPlanes == 1) {
+ out[x / 8] |= (pix << (x & 7));
+ } else if (nPlanes == 2) {
+ out[x / 4] |= (pix << ((x & 3) << 1));
+ } else if (nPlanes == 4) {
+ out[x / 2] |= (pix << ((x & 1) << 2));
}
}
@@ -187,7 +184,7 @@ struct PBMLoader {
_surface = &surface;
_colors = colors;
Common::IFFParser parser(&input);
- Common::Functor1Mem< Common::IFFChunk&, bool, PBMLoader > c(this, &PBMLoader::callback);
+ Common::Functor1Mem<Common::IFFChunk &, bool, PBMLoader> c(this, &PBMLoader::callback);
parser.parse(c);
}
@@ -251,7 +248,7 @@ uint32 PackBitsReadStream::read(void *dataPtr, uint32 dataSize) {
for (uint32 j = 0; j < lenW; j++) {
*out++ = _input->readByte();
}
- for ( ; lenR > lenW; lenR--) {
+ for (; lenR > lenW; lenR--) {
_input->readByte();
}
} else { // len > 128
diff --git a/graphics/module.mk b/graphics/module.mk
index 281f904b38..f560d9dc97 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -25,8 +25,10 @@ MODULE_OBJS := \
yuv_to_rgb.o \
decoders/bmp.o \
decoders/jpeg.o \
+ decoders/pcx.o \
decoders/pict.o \
- decoders/png.o
+ decoders/png.o \
+ decoders/tga.o
ifdef USE_SCALERS
MODULE_OBJS += \
diff --git a/graphics/pixelformat.h b/graphics/pixelformat.h
index e0cf6ce401..ca4ef11c17 100644
--- a/graphics/pixelformat.h
+++ b/graphics/pixelformat.h
@@ -97,7 +97,7 @@ struct PixelFormat {
}
inline void colorToARGB(uint32 color, uint8 &a, uint8 &r, uint8 &g, uint8 &b) const {
- a = ((color >> aShift) << aLoss) & 0xFF;
+ a = (aBits() == 0) ? 0xFF : (((color >> aShift) << aLoss) & 0xFF);
r = ((color >> rShift) << rLoss) & 0xFF;
g = ((color >> gShift) << gLoss) & 0xFF;
b = ((color >> bShift) << bLoss) & 0xFF;
diff --git a/graphics/primitives.cpp b/graphics/primitives.cpp
index 9834af65ba..b88db39f36 100644
--- a/graphics/primitives.cpp
+++ b/graphics/primitives.cpp
@@ -61,59 +61,21 @@ void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, i
}
}
+void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data) {
+ assert(penX > 0 && penY > 0);
-// FIXME: This is a limited version of thick line drawing
-// it draws striped lines at some angles. Better algorithm could
-// be found here:
-//
-// http://homepages.enterprise.net/murphy/thickline/index.html
-//
-// Feel free to replace it with better implementation
-void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data) {
- const bool steep = ABS(y1 - y0) > ABS(x1 - x0);
-
- if (steep) {
- SWAP(x0, y0);
- SWAP(x1, y1);
- }
-
- float dx = x1 - x0;
- float dy = y1 - y0;
- float d = (float)sqrt(dx * dx + dy * dy);
-
- if (!d)
+ // Shortcut
+ if (penX == 1 && penY == 1) {
+ drawLine(x0, y0, x1, y1, color, plotProc, data);
return;
-
- int thickX = (int)((float)thickness * dy / d / 2);
- int thickY = (int)((float)thickness * dx / d / 2);
-
- const int delta_x = ABS(x1 - x0);
- const int delta_y = ABS(y1 - y0);
- const int delta_err = delta_y;
- int x = x0;
- int y = y0;
- int err = 0;
-
- const int x_step = (x0 < x1) ? 1 : -1;
- const int y_step = (y0 < y1) ? 1 : -1;
-
- if (steep)
- drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data);
- else
- drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data);
-
- while (x != x1) {
- x += x_step;
- err += delta_err;
- if (2 * err > delta_x) {
- y += y_step;
- err -= delta_x;
- }
- if (steep)
- drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data);
- else
- drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data);
}
+
+ // TODO: Optimize this. It currently is a very naive way of handling
+ // thick lines since quite often it will be drawing to the same pixel
+ // multiple times.
+ for (int x = 0; x < penX; x++)
+ for (int y = 0; y < penY; y++)
+ drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data);
}
} // End of namespace Graphics
diff --git a/graphics/primitives.h b/graphics/primitives.h
index 0ab2dabcd8..f0780afc2e 100644
--- a/graphics/primitives.h
+++ b/graphics/primitives.h
@@ -25,7 +25,7 @@
namespace Graphics {
void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data);
-void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data);
+void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data);
} // End of namespace Graphics
diff --git a/graphics/scaler.cpp b/graphics/scaler.cpp
index 9ade0e6c57..b81e8937a8 100644
--- a/graphics/scaler.cpp
+++ b/graphics/scaler.cpp
@@ -167,12 +167,12 @@ void DestroyScalers(){
void Normal1x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height) {
// Spot the case when it can all be done in 1 hit
- if ((srcPitch == sizeof(OverlayColor) * (uint)width) && (dstPitch == sizeof(OverlayColor) * (uint)width)) {
- memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width * height);
+ if ((srcPitch == sizeof(uint16) * (uint)width) && (dstPitch == sizeof(uint16) * (uint)width)) {
+ memcpy(dstPtr, srcPtr, sizeof(uint16) * width * height);
return;
}
while (height--) {
- memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width);
+ memcpy(dstPtr, srcPtr, sizeof(uint16) * width);
srcPtr += srcPitch;
dstPtr += dstPitch;
}
@@ -207,11 +207,10 @@ void Normal2x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPit
uint8 *r;
assert(IS_ALIGNED(dstPtr, 4));
- assert(sizeof(OverlayColor) == 2);
while (height--) {
r = dstPtr;
for (int i = 0; i < width; ++i, r += 4) {
- uint32 color = *(((const OverlayColor *)srcPtr) + i);
+ uint32 color = *(((const uint16 *)srcPtr) + i);
color |= color << 16;
diff --git a/graphics/scaler/aspect.cpp b/graphics/scaler/aspect.cpp
index 7ad37b1ba8..327e7c5e89 100644
--- a/graphics/scaler/aspect.cpp
+++ b/graphics/scaler/aspect.cpp
@@ -23,6 +23,13 @@
#include "graphics/scaler/intern.h"
#include "graphics/scaler/aspect.h"
+#ifdef OPENPANDORA
+#define NEON_ASPECT_CORRECTOR
+#endif
+
+#ifdef NEON_ASPECT_CORRECTOR
+#include <arm_neon.h>
+#endif
#define kSuperFastAndUglyAspectMode 0 // No interpolation at all, but super-fast
#define kVeryFastAndGoodAspectMode 1 // Good quality with very good speed
@@ -55,13 +62,66 @@ static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint1
#if ASPECT_MODE == kVeryFastAndGoodAspectMode
+#ifdef NEON_ASPECT_CORRECTOR
+
+template<typename ColorMask>
+static void interpolate5LineNeon(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width, int k1, int k2) {
+ uint16x4_t kRedBlueMask_4 = vdup_n_u16(ColorMask::kRedBlueMask);
+ uint16x4_t kGreenMask_4 = vdup_n_u16(ColorMask::kGreenMask);
+ uint16x4_t k1_4 = vdup_n_u16(k1);
+ uint16x4_t k2_4 = vdup_n_u16(k2);
+ while (width >= 4) {
+ uint16x4_t srcA_4 = vld1_u16(srcA);
+ uint16x4_t srcB_4 = vld1_u16(srcB);
+ uint16x4_t p1_4 = srcB_4;
+ uint16x4_t p2_4 = srcA_4;
+
+ uint16x4_t p1_rb_4 = vand_u16(p1_4, kRedBlueMask_4);
+ uint16x4_t p1_g_4 = vand_u16(p1_4, kGreenMask_4);
+ uint16x4_t p2_rb_4 = vand_u16(p2_4, kRedBlueMask_4);
+ uint16x4_t p2_g_4 = vand_u16(p2_4, kGreenMask_4);
+
+ uint32x4_t tmp_rb_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_rb_4, k2_4), p1_rb_4, k1_4), 3);
+ uint32x4_t tmp_g_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_g_4, k2_4), p1_g_4, k1_4), 3);
+ uint16x4_t p_rb_4 = vmovn_u32(tmp_rb_4);
+ p_rb_4 = vand_u16(p_rb_4, kRedBlueMask_4);
+ uint16x4_t p_g_4 = vmovn_u32(tmp_g_4);
+ p_g_4 = vand_u16(p_g_4, kGreenMask_4);
+
+ uint16x4_t result_4 = p_rb_4 | p_g_4;
+ vst1_u16(dst, result_4);
+
+ dst += 4;
+ srcA += 4;
+ srcB += 4;
+ width -= 4;
+ }
+}
+#endif
+
template<typename ColorMask, int scale>
-static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) {
+static void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) {
if (scale == 1) {
+#ifdef NEON_ASPECT_CORRECTOR
+ int width4 = width & ~3;
+ interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 7, 1);
+ srcA += width4;
+ srcB += width4;
+ dst += width4;
+ width -= width4;
+#endif
while (width--) {
*dst++ = interpolate16_7_1<ColorMask>(*srcB++, *srcA++);
}
} else {
+#ifdef NEON_ASPECT_CORRECTOR
+ int width4 = width & ~3;
+ interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 5, 3);
+ srcA += width4;
+ srcB += width4;
+ dst += width4;
+ width -= width4;
+#endif
while (width--) {
*dst++ = interpolate16_5_3<ColorMask>(*srcB++, *srcA++);
}
@@ -160,14 +220,14 @@ int stretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, i
#if ASPECT_MODE == kSuperFastAndUglyAspectMode
if (srcPtr == dstPtr)
break;
- memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width);
+ memcpy(dstPtr, srcPtr, sizeof(uint16) * width);
#else
// Bilinear filter
switch (y % 6) {
case 0:
case 5:
if (srcPtr != dstPtr)
- memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width);
+ memcpy(dstPtr, srcPtr, sizeof(uint16) * width);
break;
case 1:
interpolate5Line<ColorMask, 1>((uint16 *)dstPtr, (const uint16 *)(srcPtr - pitch), (const uint16 *)srcPtr, width);
@@ -206,13 +266,13 @@ void Normal1xAspectTemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
#if ASPECT_MODE == kSuperFastAndUglyAspectMode
if ((y % 6) == 5)
srcPtr -= srcPitch;
- memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width);
+ memcpy(dstPtr, srcPtr, sizeof(uint16) * width);
#else
// Bilinear filter five input lines onto six output lines
switch (y % 6) {
case 0:
// First output line is copied from first input line
- memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width);
+ memcpy(dstPtr, srcPtr, sizeof(uint16) * width);
break;
case 1:
// Second output line is mixed from first and second input line
@@ -233,7 +293,7 @@ void Normal1xAspectTemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
case 5:
// Sixth (and last) output line is copied from fifth (and last) input line
srcPtr -= srcPitch;
- memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width);
+ memcpy(dstPtr, srcPtr, sizeof(uint16) * width);
break;
}
#endif
diff --git a/graphics/sjis.h b/graphics/sjis.h
index 2d05005fc3..928332f712 100644
--- a/graphics/sjis.h
+++ b/graphics/sjis.h
@@ -169,7 +169,7 @@ protected:
bool _flippedMode;
int _fontWidth, _fontHeight;
uint8 _bitPosNewLineMask;
-
+
bool isASCII(uint16 ch) const;
virtual const uint8 *getCharData(uint16 c) const = 0;
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index c0f1046eae..41ae8dcebb 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -26,6 +26,7 @@
#include "common/textconsole.h"
#include "graphics/primitives.h"
#include "graphics/surface.h"
+#include "graphics/conversion.h"
namespace Graphics {
@@ -49,6 +50,17 @@ void Surface::drawLine(int x0, int y0, int x1, int y1, uint32 color) {
error("Surface::drawLine: bytesPerPixel must be 1, 2, or 4");
}
+void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) {
+ if (format.bytesPerPixel == 1)
+ Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<byte>, this);
+ else if (format.bytesPerPixel == 2)
+ Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint16>, this);
+ else if (format.bytesPerPixel == 4)
+ Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint32>, this);
+ else
+ error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4");
+}
+
void Surface::create(uint16 width, uint16 height, const PixelFormat &f) {
free();
@@ -271,6 +283,72 @@ void Surface::move(int dx, int dy, int height) {
}
}
+void Surface::convertToInPlace(const PixelFormat &dstFormat, const byte *palette) {
+ // Do not convert to the same format and ignore empty surfaces.
+ if (format == dstFormat || pixels == 0) {
+ return;
+ }
+
+ if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
+ error("Surface::convertToInPlace(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
+
+ if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
+ error("Surface::convertToInPlace(): Can only convert to 2Bpp and 4Bpp");
+
+ // In case the surface data needs more space allocate it.
+ if (dstFormat.bytesPerPixel > format.bytesPerPixel) {
+ void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
+ if (!newPixels) {
+ error("Surface::convertToInPlace(): Out of memory");
+ }
+ pixels = newPixels;
+ }
+
+ // We take advantage of the fact that pitch is always w * format.bytesPerPixel.
+ // This is assured by the logic of Surface::create.
+
+ // We need to handle 1 Bpp surfaces special here.
+ if (format.bytesPerPixel == 1) {
+ assert(palette);
+
+ for (int y = h; y > 0; --y) {
+ const byte *srcRow = (const byte *)pixels + y * pitch - 1;
+ byte *dstRow = (byte *)pixels + y * w * dstFormat.bytesPerPixel - dstFormat.bytesPerPixel;
+
+ for (int x = 0; x < w; x++) {
+ byte index = *srcRow--;
+ byte r = palette[index * 3];
+ byte g = palette[index * 3 + 1];
+ byte b = palette[index * 3 + 2];
+
+ uint32 color = dstFormat.RGBToColor(r, g, b);
+
+ if (dstFormat.bytesPerPixel == 2)
+ *((uint16 *)dstRow) = color;
+ else
+ *((uint32 *)dstRow) = color;
+
+ dstRow -= dstFormat.bytesPerPixel;
+ }
+ }
+ } else {
+ crossBlit((byte *)pixels, (const byte *)pixels, w * dstFormat.bytesPerPixel, pitch, w, h, dstFormat, format);
+ }
+
+ // In case the surface data got smaller, free up some memory.
+ if (dstFormat.bytesPerPixel < format.bytesPerPixel) {
+ void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel);
+ if (!newPixels) {
+ error("Surface::convertToInPlace(): Freeing memory failed");
+ }
+ pixels = newPixels;
+ }
+
+ // Update the surface specific data.
+ format = dstFormat;
+ pitch = w * dstFormat.bytesPerPixel;
+}
+
Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
assert(pixels);
diff --git a/graphics/surface.h b/graphics/surface.h
index eb8d1ac42e..6c9e464657 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -137,6 +137,20 @@ struct Surface {
/**
* Convert the data to another pixel format.
*
+ * This works in-place. This means it will not create an additional buffer
+ * for the conversion process. The value of pixels might change though.
+ *
+ * Note that you should only use this, when you created the Surface data via
+ * create! Otherwise this function has undefined behavior.
+ *
+ * @param dstFormat The desired format
+ * @param palette The palette (in RGB888), if the source format has a Bpp of 1
+ */
+ void convertToInPlace(const PixelFormat &dstFormat, const byte *palette = 0);
+
+ /**
+ * Convert the data to another pixel format.
+ *
* The calling code must call free on the returned surface and then delete
* it.
*
@@ -153,10 +167,26 @@ struct Surface {
* @param x1 The x coordinate of the end point.
* @param y1 The y coordinate of the end point.
* @param color The color of the line.
+ * @note This is just a wrapper around Graphics::drawLine
*/
void drawLine(int x0, int y0, int x1, int y1, uint32 color);
/**
+ * Draw a thick line.
+ *
+ * @param x0 The x coordinate of the start point.
+ * @param y0 The y coordiante of the start point.
+ * @param x1 The x coordinate of the end point.
+ * @param y1 The y coordinate of the end point.
+ * @param penX The width of the pen (thickness in the x direction)
+ * @param penY The height of the pen (thickness in the y direction)
+ * @param color The color of the line.
+ * @note This is just a wrapper around Graphics::drawThickLine
+ * @note The x/y coordinates of the start and end points are the upper-left most part of the pen
+ */
+ void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color);
+
+ /**
* Draw a horizontal line.
*
* @param x The start x coordinate of the line.
diff --git a/graphics/wincursor.cpp b/graphics/wincursor.cpp
index 2db72a2874..1d599f7130 100644
--- a/graphics/wincursor.cpp
+++ b/graphics/wincursor.cpp
@@ -30,6 +30,46 @@
namespace Graphics {
+/** A Windows cursor. */
+class WinCursor : public Cursor {
+public:
+ WinCursor();
+ ~WinCursor();
+
+ /** Return the cursor's width. */
+ uint16 getWidth() const;
+ /** Return the cursor's height. */
+ uint16 getHeight() const;
+ /** Return the cursor's hotspot's x coordinate. */
+ uint16 getHotspotX() const;
+ /** Return the cursor's hotspot's y coordinate. */
+ uint16 getHotspotY() const;
+ /** Return the cursor's transparent key. */
+ byte getKeyColor() const;
+
+ const byte *getSurface() const { return _surface; }
+
+ const byte *getPalette() const { return _palette; }
+ byte getPaletteStartIndex() const { return 0; }
+ uint16 getPaletteCount() const { return 256; }
+
+ /** Read the cursor's data out of a stream. */
+ bool readFromStream(Common::SeekableReadStream &stream);
+
+private:
+ byte *_surface;
+ byte _palette[256 * 3];
+
+ uint16 _width; ///< The cursor's width.
+ uint16 _height; ///< The cursor's height.
+ uint16 _hotspotX; ///< The cursor's hotspot's x coordinate.
+ uint16 _hotspotY; ///< The cursor's hotspot's y coordinate.
+ byte _keyColor; ///< The cursor's transparent key
+
+ /** Clear the cursor. */
+ void clear();
+};
+
WinCursor::WinCursor() {
_width = 0;
_height = 0;
diff --git a/graphics/wincursor.h b/graphics/wincursor.h
index e6b35dc80c..9e73e3a12f 100644
--- a/graphics/wincursor.h
+++ b/graphics/wincursor.h
@@ -36,46 +36,6 @@ class SeekableReadStream;
namespace Graphics {
-/** A Windows cursor. */
-class WinCursor : public Cursor {
-public:
- WinCursor();
- ~WinCursor();
-
- /** Return the cursor's width. */
- uint16 getWidth() const;
- /** Return the cursor's height. */
- uint16 getHeight() const;
- /** Return the cursor's hotspot's x coordinate. */
- uint16 getHotspotX() const;
- /** Return the cursor's hotspot's y coordinate. */
- uint16 getHotspotY() const;
- /** Return the cursor's transparent key. */
- byte getKeyColor() const;
-
- const byte *getSurface() const { return _surface; }
-
- const byte *getPalette() const { return _palette; }
- byte getPaletteStartIndex() const { return 0; }
- uint16 getPaletteCount() const { return 256; }
-
- /** Read the cursor's data out of a stream. */
- bool readFromStream(Common::SeekableReadStream &stream);
-
-private:
- byte *_surface;
- byte _palette[256 * 3];
-
- uint16 _width; ///< The cursor's width.
- uint16 _height; ///< The cursor's height.
- uint16 _hotspotX; ///< The cursor's hotspot's x coordinate.
- uint16 _hotspotY; ///< The cursor's hotspot's y coordinate.
- byte _keyColor; ///< The cursor's transparent key
-
- /** Clear the cursor. */
- void clear();
-};
-
/**
* A structure holding an array of cursors from a single Windows Executable cursor group.
*
@@ -91,7 +51,7 @@ struct WinCursorGroup {
struct CursorItem {
Common::WinResourceID id;
- WinCursor *cursor;
+ Cursor *cursor;
};
Common::Array<CursorItem> cursors;
diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp
index ac7f217fee..6043315a13 100644
--- a/graphics/yuv_to_rgb.cpp
+++ b/graphics/yuv_to_rgb.cpp
@@ -83,129 +83,127 @@
// BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-#include "common/scummsys.h"
-#include "common/singleton.h"
-
#include "graphics/surface.h"
+#include "graphics/yuv_to_rgb.h"
+
+namespace Common {
+DECLARE_SINGLETON(Graphics::YUVToRGBManager);
+}
namespace Graphics {
class YUVToRGBLookup {
public:
- YUVToRGBLookup(Graphics::PixelFormat format);
- ~YUVToRGBLookup();
+ YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale);
- int16 *_colorTab;
- uint32 *_rgbToPix;
-};
+ Graphics::PixelFormat getFormat() const { return _format; }
+ YUVToRGBManager::LuminanceScale getScale() const { return _scale; }
+ const uint32 *getRGBToPix() const { return _rgbToPix; }
-YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format) {
- _colorTab = new int16[4 * 256]; // 2048 bytes
-
- int16 *Cr_r_tab = &_colorTab[0 * 256];
- int16 *Cr_g_tab = &_colorTab[1 * 256];
- int16 *Cb_g_tab = &_colorTab[2 * 256];
- int16 *Cb_b_tab = &_colorTab[3 * 256];
+private:
+ Graphics::PixelFormat _format;
+ YUVToRGBManager::LuminanceScale _scale;
+ uint32 _rgbToPix[3 * 768]; // 9216 bytes
+};
- _rgbToPix = new uint32[3 * 768]; // 9216 bytes
+YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) {
+ _format = format;
+ _scale = scale;
uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768];
uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768];
uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768];
- int16 CR, CB;
- int i;
+ if (scale == YUVToRGBManager::kScaleFull) {
+ // Set up entries 0-255 in rgb-to-pixel value tables.
+ for (int i = 0; i < 256; i++) {
+ r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0);
+ g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0);
+ b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i);
+ }
+
+ // Spread out the values we have to the rest of the array so that we do
+ // not need to check for overflow.
+ for (int i = 0; i < 256; i++) {
+ r_2_pix_alloc[i] = r_2_pix_alloc[256];
+ r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
+ g_2_pix_alloc[i] = g_2_pix_alloc[256];
+ g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
+ b_2_pix_alloc[i] = b_2_pix_alloc[256];
+ b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
+ }
+ } else {
+ // Set up entries 16-235 in rgb-to-pixel value tables
+ for (int i = 16; i < 236; i++) {
+ int scaledValue = (i - 16) * 255 / 219;
+ r_2_pix_alloc[i + 256] = format.RGBToColor(scaledValue, 0, 0);
+ g_2_pix_alloc[i + 256] = format.RGBToColor(0, scaledValue, 0);
+ b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, scaledValue);
+ }
+
+ // Spread out the values we have to the rest of the array so that we do
+ // not need to check for overflow. We have to do it here in two steps.
+ for (int i = 0; i < 256 + 16; i++) {
+ r_2_pix_alloc[i] = r_2_pix_alloc[256 + 16];
+ g_2_pix_alloc[i] = g_2_pix_alloc[256 + 16];
+ b_2_pix_alloc[i] = b_2_pix_alloc[256 + 16];
+ }
+
+ for (int i = 256 + 236; i < 768; i++) {
+ r_2_pix_alloc[i] = r_2_pix_alloc[256 + 236 - 1];
+ g_2_pix_alloc[i] = g_2_pix_alloc[256 + 236 - 1];
+ b_2_pix_alloc[i] = b_2_pix_alloc[256 + 236 - 1];
+ }
+ }
+}
+
+YUVToRGBManager::YUVToRGBManager() {
+ _lookup = 0;
+
+ int16 *Cr_r_tab = &_colorTab[0 * 256];
+ int16 *Cr_g_tab = &_colorTab[1 * 256];
+ int16 *Cb_g_tab = &_colorTab[2 * 256];
+ int16 *Cb_b_tab = &_colorTab[3 * 256];
// Generate the tables for the display surface
- for (i = 0; i < 256; i++) {
+ for (int i = 0; i < 256; i++) {
// Gamma correction (luminescence table) and chroma correction
// would be done here. See the Berkeley mpeg_play sources.
- CR = CB = (i - 128);
+ int16 CR = (i - 128), CB = CR;
Cr_r_tab[i] = (int16) ( (0.419 / 0.299) * CR) + 0 * 768 + 256;
Cr_g_tab[i] = (int16) (-(0.299 / 0.419) * CR) + 1 * 768 + 256;
Cb_g_tab[i] = (int16) (-(0.114 / 0.331) * CB);
Cb_b_tab[i] = (int16) ( (0.587 / 0.331) * CB) + 2 * 768 + 256;
}
-
- // Set up entries 0-255 in rgb-to-pixel value tables.
- for (i = 0; i < 256; i++) {
- r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0);
- g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0);
- b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i);
- }
-
- // Spread out the values we have to the rest of the array so that we do
- // not need to check for overflow.
- for (i = 0; i < 256; i++) {
- r_2_pix_alloc[i] = r_2_pix_alloc[256];
- r_2_pix_alloc[i + 512] = r_2_pix_alloc[511];
- g_2_pix_alloc[i] = g_2_pix_alloc[256];
- g_2_pix_alloc[i + 512] = g_2_pix_alloc[511];
- b_2_pix_alloc[i] = b_2_pix_alloc[256];
- b_2_pix_alloc[i + 512] = b_2_pix_alloc[511];
- }
-}
-
-YUVToRGBLookup::~YUVToRGBLookup() {
- delete[] _rgbToPix;
- delete[] _colorTab;
-}
-
-class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> {
-public:
- const YUVToRGBLookup *getLookup(Graphics::PixelFormat format);
-
-private:
- friend class Common::Singleton<SingletonBaseType>;
- YUVToRGBManager();
- ~YUVToRGBManager();
-
- Graphics::PixelFormat _lastFormat;
- YUVToRGBLookup *_lookup;
-};
-
-YUVToRGBManager::YUVToRGBManager() {
- _lookup = 0;
}
YUVToRGBManager::~YUVToRGBManager() {
delete _lookup;
}
-const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format) {
- if (_lastFormat == format)
+const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) {
+ if (_lookup && _lookup->getFormat() == format && _lookup->getScale() == scale)
return _lookup;
delete _lookup;
- _lookup = new YUVToRGBLookup(format);
- _lastFormat = format;
+ _lookup = new YUVToRGBLookup(format, scale);
return _lookup;
}
-} // End of namespace Graphics
-
-namespace Common {
-DECLARE_SINGLETON(Graphics::YUVToRGBManager);
-}
-
-#define YUVToRGBMan (Graphics::YUVToRGBManager::instance())
-
-namespace Graphics {
-
#define PUT_PIXEL(s, d) \
L = &rgbToPix[(s)]; \
*((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b])
template<typename PixelInt>
-void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Keep the tables in pointers here to avoid a dereference on each pixel
- const int16 *Cr_r_tab = lookup->_colorTab;
+ const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
- const uint32 *rgbToPix = lookup->_rgbToPix;
+ const uint32 *rgbToPix = lookup->getRGBToPix();
for (int h = 0; h < yHeight; h++) {
for (int w = 0; w < yWidth; w++) {
@@ -229,32 +227,32 @@ void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
}
}
-void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void YUVToRGBManager::convert444(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(ySrc && uSrc && vSrc);
- const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
+ const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
template<typename PixelInt>
-void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
int halfHeight = yHeight >> 1;
int halfWidth = yWidth >> 1;
// Keep the tables in pointers here to avoid a dereference on each pixel
- const int16 *Cr_r_tab = lookup->_colorTab;
+ const int16 *Cr_r_tab = colorTab;
const int16 *Cr_g_tab = Cr_r_tab + 256;
const int16 *Cb_g_tab = Cr_g_tab + 256;
const int16 *Cb_b_tab = Cb_g_tab + 256;
- const uint32 *rgbToPix = lookup->_rgbToPix;
+ const uint32 *rgbToPix = lookup->getRGBToPix();
for (int h = 0; h < halfHeight; h++) {
for (int w = 0; w < halfWidth; w++) {
@@ -283,7 +281,7 @@ void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
}
}
-void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+void YUVToRGBManager::convert420(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
assert(dst && dst->pixels);
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
@@ -291,13 +289,98 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS
assert((yWidth & 1) == 0);
assert((yHeight & 1) == 0);
- const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
+ const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
+
+ // Use a templated function to avoid an if check on every pixel
+ if (dst->format.bytesPerPixel == 2)
+ convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ else
+ convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+}
+
+#define READ_QUAD(ptr, prefix) \
+ byte prefix##A = ptr[index]; \
+ byte prefix##B = ptr[index + 1]; \
+ byte prefix##C = ptr[index + uvPitch]; \
+ byte prefix##D = ptr[index + uvPitch + 1]
+
+#define DO_INTERPOLATION(out) \
+ out = (out##A * (4 - xDiff) * (4 - yDiff) + out##B * xDiff * (4 - yDiff) + \
+ out##C * yDiff * (4 - xDiff) + out##D * xDiff * yDiff) >> 4
+
+#define DO_YUV410_PIXEL() \
+ DO_INTERPOLATION(u); \
+ DO_INTERPOLATION(v); \
+ \
+ cr_r = Cr_r_tab[v]; \
+ crb_g = Cr_g_tab[v] + Cb_g_tab[u]; \
+ cb_b = Cb_b_tab[u]; \
+ \
+ PUT_PIXEL(*ySrc, dstPtr); \
+ dstPtr += sizeof(PixelInt); \
+ \
+ ySrc++; \
+ xDiff++
+
+template<typename PixelInt>
+void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+ // Keep the tables in pointers here to avoid a dereference on each pixel
+ const int16 *Cr_r_tab = colorTab;
+ const int16 *Cr_g_tab = Cr_r_tab + 256;
+ const int16 *Cb_g_tab = Cr_g_tab + 256;
+ const int16 *Cb_b_tab = Cb_g_tab + 256;
+ const uint32 *rgbToPix = lookup->getRGBToPix();
+
+ int quarterWidth = yWidth >> 2;
+
+ for (int y = 0; y < yHeight; y++) {
+ for (int x = 0; x < quarterWidth; x++) {
+ // Perform bilinear interpolation on the the chroma values
+ // Based on the algorithm found here: http://tech-algorithm.com/articles/bilinear-image-scaling/
+ // Feel free to optimize further
+ int targetY = y >> 2;
+ int xDiff = 0;
+ int yDiff = y & 3;
+ int index = targetY * uvPitch + x;
+
+ // Declare some variables for the following macros
+ byte u, v;
+ int16 cr_r, crb_g, cb_b;
+ register const uint32 *L;
+
+ READ_QUAD(uSrc, u);
+ READ_QUAD(vSrc, v);
+
+ DO_YUV410_PIXEL();
+ DO_YUV410_PIXEL();
+ DO_YUV410_PIXEL();
+ DO_YUV410_PIXEL();
+ }
+
+ dstPtr += dstPitch - yWidth * sizeof(PixelInt);
+ ySrc += yPitch - yWidth;
+ }
+}
+
+#undef READ_QUAD
+#undef DO_INTERPOLATION
+#undef DO_YUV410_PIXEL
+
+void YUVToRGBManager::convert410(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+ // Sanity checks
+ assert(dst && dst->pixels);
+ assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
+ assert(ySrc && uSrc && vSrc);
+ assert((yWidth & 3) == 0);
+ assert((yHeight & 3) == 0);
+
+ const YUVToRGBLookup *lookup = getLookup(dst->format, scale);
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
} // End of namespace Graphics
diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h
index 8e025042dc..f785422c5a 100644
--- a/graphics/yuv_to_rgb.h
+++ b/graphics/yuv_to_rgb.h
@@ -32,38 +32,85 @@
#define GRAPHICS_YUV_TO_RGB_H
#include "common/scummsys.h"
+#include "common/singleton.h"
#include "graphics/surface.h"
namespace Graphics {
-/**
- * Convert a YUV444 image to an RGB surface
- *
- * @param dst the destination surface
- * @param ySrc the source of the y component
- * @param uSrc the source of the u component
- * @param vSrc the source of the v component
- * @param yWidth the width of the y surface
- * @param yHeight the height of the y surface
- * @param yPitch the pitch of the y surface
- * @param uvPitch the pitch of the u and v surfaces
- */
-void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+class YUVToRGBLookup;
-/**
- * Convert a YUV420 image to an RGB surface
- *
- * @param dst the destination surface
- * @param ySrc the source of the y component
- * @param uSrc the source of the u component
- * @param vSrc the source of the v component
- * @param yWidth the width of the y surface (must be divisible by 2)
- * @param yHeight the height of the y surface (must be divisible by 2)
- * @param yPitch the pitch of the y surface
- * @param uvPitch the pitch of the u and v surfaces
- */
-void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> {
+public:
+ /** The scale of the luminance values */
+ enum LuminanceScale {
+ kScaleFull, /** Luminance values range from [0, 255] */
+ kScaleITU /** Luminance values range from [16, 235], the range from ITU-R BT.601 */
+ };
+
+ /**
+ * Convert a YUV444 image to an RGB surface
+ *
+ * @param dst the destination surface
+ * @param scale the scale of the luminance values
+ * @param ySrc the source of the y component
+ * @param uSrc the source of the u component
+ * @param vSrc the source of the v component
+ * @param yWidth the width of the y surface
+ * @param yHeight the height of the y surface
+ * @param yPitch the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
+ void convert444(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+ /**
+ * Convert a YUV420 image to an RGB surface
+ *
+ * @param dst the destination surface
+ * @param scale the scale of the luminance values
+ * @param ySrc the source of the y component
+ * @param uSrc the source of the u component
+ * @param vSrc the source of the v component
+ * @param yWidth the width of the y surface (must be divisible by 2)
+ * @param yHeight the height of the y surface (must be divisible by 2)
+ * @param yPitch the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
+ void convert420(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+ /**
+ * Convert a YUV410 image to an RGB surface
+ *
+ * Since the chroma has a very low resolution in 410, we perform bilinear scaling
+ * on the two chroma planes to produce the image. The chroma planes must have
+ * at least one extra row and one extra column that can be read from in order to
+ * produce a proper image. It is suggested that you fill these in with the previous
+ * row and column's data. This is required in order to speed up this function.
+ *
+ * @param dst the destination surface
+ * @param scale the scale of the luminance values
+ * @param ySrc the source of the y component
+ * @param uSrc the source of the u component
+ * @param vSrc the source of the v component
+ * @param yWidth the width of the y surface (must be divisible by 4)
+ * @param yHeight the height of the y surface (must be divisible by 4)
+ * @param yPitch the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
+ void convert410(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+private:
+ friend class Common::Singleton<SingletonBaseType>;
+ YUVToRGBManager();
+ ~YUVToRGBManager();
+
+ const YUVToRGBLookup *getLookup(Graphics::PixelFormat format, LuminanceScale scale);
+
+ YUVToRGBLookup *_lookup;
+ int16 _colorTab[4 * 256]; // 2048 bytes
+};
} // End of namespace Graphics
+#define YUVToRGBMan (::Graphics::YUVToRGBManager::instance())
+
#endif