blob: 0119ffb0cc61367923afa31ebcb6a4447c665afb [file] [log] [blame]
/*
* Project: VizKit
* Version: 2.3
* Date: 20090823
* File: VisualImage.cpp
*
*/
/***************************************************************************
Copyright (c) 2004-2009 Heiko Wichmann (http://www.imagomat.de/vizkit)
This software is provided 'as-is', without any expressed 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.
***************************************************************************/
#include "VisualImage.h"
#include "VisualTextureContainer.h"
#include "VisualStyledString.h"
#include "VisualFile.h"
#include "VisualGraphics.h"
#include "VisualErrorHandling.h"
#include "VisualNotification.h"
#include "VisualNetwork.h"
#include "VisualDispatch.h"
#include "VisualColorTools.h"
#if TARGET_OS_WIN
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#endif
using namespace VizKit;
VisualImage::VisualImage() {
visualTextureContainer = new VisualTextureContainer;
isSet = false;
blendMode = kBlend;
histogram = NULL;
}
VisualImage::~VisualImage() {
delete visualTextureContainer;
if (histogram != NULL) {
delete histogram;
}
}
VisualImage::VisualImage(const VisualImage& other) : VisualObject(other) {
copy(other);
}
VisualImage& VisualImage::operator=(const VisualImage& other) {
if (this == &other) return *this;
VisualObject::operator=(other);
delete this->visualTextureContainer;
if (this->histogram != NULL) {
delete this->histogram;
}
this->copy(other);
return *this;
}
VisualImage* VisualImage::clone(void) const {
return new VisualImage(*this);
}
VisualImage* VisualImage::cloneIndependently() const {
VisualTextureContainer* textureContainer = this->getTextureContainer();
PixelColor* pixels = textureContainer->createARGBImagePixels();
VisualImage* imageCopy = VisualImage::createWithARGBPixelData(pixels, textureContainer->getImageWidth(), textureContainer->getImageHeight());
free(pixels);
return imageCopy;
}
bool VisualImage::initWithFile(VisualFile& aFile) {
bool success = this->visualTextureContainer->initWithFile(aFile);
if (success) {
this->isSet = true;
}
return success;
}
bool VisualImage::initWithARGBPixelData(PixelColor* argbPixels, uint32 width, uint32 height) {
bool success = true;
success = this->visualTextureContainer->initWithARGBPixelData(argbPixels, width, height);
if (success) {
this->isSet = true;
}
return success;
}
bool VisualImage::initWithEncodedData(const char* const bufferData, size_t size) {
bool success = this->visualTextureContainer->initWithEncodedData(bufferData, size);
if (success) {
this->isSet = true;
}
return success;
}
bool VisualImage::initWithStyledString(VisualStyledString& styledString) {
bool success = this->visualTextureContainer->initWithStyledString(styledString);
if (success) {
this->isSet = true;
}
return success;
}
#if TARGET_OS_WIN
bool VisualImage::initWithResource(int nameId) {
bool success = true;
char* type = "PNG";
char* pngImageData = NULL;
uint32 sizeOfImageResource = 0;
success = VisualFile::getDataOfResource(nameId, type, (void**)&pngImageData, sizeOfImageResource);
if (success) {
success = this->visualTextureContainer->initWithEncodedData((const char* const)pngImageData, sizeOfImageResource);
BOOL deleteSuccess = DeleteObject(pngImageData);
} else {
char errLog[256];
sprintf(errLog, "Err: Unable to get resource data %s in file: %s (line: %d) [%s])", __FILE__, __LINE__, __FUNCTION__);
writeLog(errLog);
}
if (success) {
this->isSet = true;
}
return success;
}
#endif
#if TARGET_OS_MAC
bool VisualImage::initWithResource(const char* name) {
bool success = true;
VisualFile* resourceFile = VisualFile::createWithResourcesDirectory();
VisualString resourceFileName = VisualString(name);
success = resourceFile->appendFileName(resourceFileName);
if (success) {
success = this->initWithFile(*resourceFile);
delete(resourceFile);
if (success) {
this->isSet = true;
} else {
char errLog[256];
sprintf(errLog, "Err: Unable to load resource %s in file: %s (line: %d) [%s])", name, __FILE__, __LINE__, __FUNCTION__);
writeLog(errLog);
}
}
return success;
}
#endif
bool VisualImage::initWithURL(VisualString& anURL, VisualItemIdentifier& anId) {
this->visualTextureContainer->clean();
bool success = VisualNetwork::addToDownloadQueue(anURL, this, anId);
return success;
}
bool VisualImage::initWithLoadedEncodedData() {
bool success = false;
if (this->hasData()) {
success = this->visualTextureContainer->initWithEncodedData((const char*)this->getData(), this->getDataSize());
}
this->freeData();
return success;
}
bool VisualImage::initWithFramebuffer(const BottomLeftPositionedPixelRect& clipRect) {
this->visualTextureContainer->clean();
bool success = this->visualTextureContainer->initWithFramebuffer(clipRect);
return success;
}
void VisualImage::writeToPNGFileAsync(VisualFile& aVisualFile) {
// Only in the main thread we can transfer the memory of the graphics card to the CPU memory
VisualNotification aNotification;
aNotification.setPointer(this);
aNotification.setObject(aVisualFile);
aNotification.setKey(kImageWriteToPNGFileMsg);
aNotification.post();
}
void VisualImage::writeToPNGFileAsyncAndDelete(VisualFile& aVisualFile) {
// Only in the main thread we can transfer the memory of the graphics card to the CPU memory
VisualNotification aNotification;
aNotification.setPointer(this);
aNotification.setObject(aVisualFile);
aNotification.setKey(kImageWriteToPNGFileAndDeleteMsg);
aNotification.post();
}
bool VisualImage::writeToPNGFile(VisualFile& aVisualFile) const {
bool success = true;
PixelColor* texturePixels = this->visualTextureContainer->createARGBImagePixels();
uint32 imageSize = this->visualTextureContainer->getImageWidth() * this->visualTextureContainer->getImageHeight() * sizeof(PixelColor);
PixelColor* flippedImagePixels = (PixelColor*)malloc(imageSize);
for (uint32 i = 0; i < this->visualTextureContainer->getImageHeight(); i++) {
memcpy((flippedImagePixels + this->visualTextureContainer->getImageWidth() * (this->visualTextureContainer->getImageHeight() - i - 1)), (texturePixels + this->visualTextureContainer->getImageWidth() * i), this->visualTextureContainer->getImageWidth() * 4);
}
free(texturePixels);
texturePixels = NULL;
#if TARGET_OS_MAC
CFDictionaryRef options = NULL;
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, flippedImagePixels, imageSize, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGBitmapInfo bitmapInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
CGImageRef imageRef = CGImageCreate(this->visualTextureContainer->getImageWidth(), this->visualTextureContainer->getImageHeight(), 8, 32, this->visualTextureContainer->getImageWidth() * 4, colorspace, bitmapInfo, dataProvider, NULL, false, kCGRenderingIntentDefault);
CFMutableDataRef destinationImageData = CFDataCreateMutable(kCFAllocatorDefault, 0);
size_t numberOfImages = 1;
CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)destinationImageData, CFSTR("public.png"), numberOfImages, NULL);
CGImageDestinationAddImage(destination, imageRef, options);
CGImageDestinationFinalize(destination);
CGColorSpaceRelease(colorspace);
const UInt8* dataPointer = CFDataGetBytePtr(destinationImageData);
void** dataHandle = (void**)&dataPointer;
VisualFile::writeDataToFile(dataHandle, CFDataGetLength(destinationImageData), aVisualFile);
#endif
#if TARGET_OS_WIN
INT stride = sizeof(PixelColor) * this->visualTextureContainer->getImageWidth();
Gdiplus::Bitmap image(this->visualTextureContainer->getImageWidth(),
this->visualTextureContainer->getImageHeight(),
stride,
PixelFormat32bppARGB,
(BYTE*)flippedImagePixels);
IStream* pIStream = NULL;
HRESULT result = CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&pIStream);
if (result != S_OK)
return false;
CLSID pngClsid;
VisualGraphics::getGdiplusEncoderClsid(L"image/png", &pngClsid);
Gdiplus::EncoderParameters encoderParameters;
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
// setup compression level
ULONG quality = 50;
encoderParameters.Parameter[0].Value = &quality;
Gdiplus::Status saveStatus = image.Save(pIStream, &pngClsid, &encoderParameters);
if (saveStatus != Gdiplus::Ok) {
pIStream->Release();
return false;
}
// get the size of the stream
ULARGE_INTEGER ulnSize;
LARGE_INTEGER lnOffset;
lnOffset.QuadPart = 0;
if (pIStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize) != S_OK) {
pIStream->Release();
return false;
}
// now move the pointer to the beginning of the file
if (pIStream->Seek(lnOffset, STREAM_SEEK_SET, NULL) != S_OK) {
pIStream->Release();
return false;
}
HGLOBAL hg;
if (GetHGlobalFromStream(pIStream, &hg) == S_OK) {
char* dataPointer = new char[(unsigned int)ulnSize.QuadPart];
// Read the stream directly into the buffer
ULONG ulBytesRead;
if (pIStream->Read(dataPointer, (ULONG)ulnSize.QuadPart, &ulBytesRead) != S_OK) {
pIStream->Release();
return false;
}
void** dataHandle = (void**)&dataPointer;
VisualFile::writeDataToFile(dataHandle, ulBytesRead, aVisualFile);
delete[] dataPointer;
}
#endif
free(flippedImagePixels);
return success;
}
void VisualImage::applyConvolutionFilter(const VisualConvolutionFilter& aConvolutionFilter) {
this->visualTextureContainer->applyConvolutionFilter(aConvolutionFilter);
}
uint32 VisualImage::getWidth() const {
return this->visualTextureContainer->getImageWidth();
}
uint32 VisualImage::getHeight() const {
return this->visualTextureContainer->getImageHeight();
}
double VisualImage::getLogicalWidth() const {
return this->visualTextureContainer->getTextureLogicalWidth();
}
double VisualImage::getLogicalHeight() const {
return this->visualTextureContainer->getTextureLogicalHeight();
}
void VisualImage::draw(const VertexChain* const aVertexChain, bool debug) {
VisualGraphics* theVisualGraphics = VisualGraphics::getInstance();
theVisualGraphics->drawTexture(this->visualTextureContainer->getTextureName(), aVertexChain, this->visualTextureContainer->getUseRectExtension(), this->blendMode, debug);
}
bool VisualImage::isEmpty() const {
return !(this->isSet);
}
BlendMode VisualImage::getBlendMode(void) const {
return this->blendMode;
}
void VisualImage::setBlendMode(BlendMode aBlendMode) {
this->blendMode = aBlendMode;
}
VisualImage* VisualImage::createWithFile(VisualFile& aFile) {
VisualImage* anImage = new VisualImage;
bool success = anImage->initWithFile(aFile);
if (!success) {
delete anImage;
anImage = NULL;
}
return anImage;
}
VisualImage* VisualImage::createWithARGBPixelData(PixelColor* argbPixels, uint32 width, uint32 height) {
VisualImage* anImage = new VisualImage;
bool success = anImage->initWithARGBPixelData(argbPixels, width, height);
if (!success) {
delete anImage;
anImage = NULL;
}
return anImage;
}
VisualImage* VisualImage::createWithEncodedData(const char* const bufferData, uint32 size) {
VisualImage* anImage = new VisualImage;
bool success = anImage->initWithEncodedData(bufferData, size);
if (!success) {
delete anImage;
anImage = NULL;
}
return anImage;
}
VisualImage* VisualImage::createWithStyledString(VisualStyledString& styledString) {
VisualImage* anImage = new VisualImage;
bool success = anImage->initWithStyledString(styledString);
if (!success) {
delete anImage;
anImage = NULL;
}
return anImage;
}
#if TARGET_OS_MAC
VisualImage* VisualImage::createWithResource(const char* name) {
#endif
#if TARGET_OS_WIN
VisualImage* VisualImage::createWithResource(int nameId) {
#endif
VisualImage* anImage = new VisualImage;
#if TARGET_OS_MAC
bool success = anImage->initWithResource(name);
#endif
#if TARGET_OS_WIN
bool success = anImage->initWithResource(nameId);
#endif
if (!success) {
delete anImage;
anImage = NULL;
}
return anImage;
}
VisualImage* VisualImage::createWithURL(VisualString& anURL, VisualItemIdentifier& anId) {
VisualImage* anImage = new VisualImage;
bool success = anImage->initWithURL(anURL, anId);
if (!success) {
delete anImage;
anImage = NULL;
}
return anImage;
}
VisualImage* VisualImage::createWithFramebuffer(const BottomLeftPositionedPixelRect& clipRect) {
VisualImage* anImage = new VisualImage;
bool success = anImage->initWithFramebuffer(clipRect);
if (!success) {
delete anImage;
anImage = NULL;
}
return anImage;
}
void VisualImage::resample(const PixelRect& pixelRect) {
this->visualTextureContainer->resample(pixelRect);
}
VisualTextureContainer* VisualImage::getTextureContainer() const {
return this->visualTextureContainer;
}
void VisualImage::dataLoadDidEnd(const VisualItemIdentifier& identifier) {
// Only in the main thread we can transfer the fetched encoded image to the memory of the graphics card
// (OpenGL calls must be executed in the main thread)
VisualNotification aNotification(identifier);
aNotification.setPointer(this);
aNotification.setKey(kLoadingEncodedImageDataCompletedMsg);
aNotification.post();
}
void VisualImage::createHistogram() {
VisualImageHistogramPixelColors* visualImageHistogramPixelColors = new VisualImageHistogramPixelColors;
visualImageHistogramPixelColors->visualImage = this;
this->getRGBHistogramInputPixels(visualImageHistogramPixelColors->pixelColorValuesVector);
bool success = false;
success = VisualThreading::createThread((ThreadingFuncPtr)VisualImage::createHistogramOfRGBPixelsThread, (void*)visualImageHistogramPixelColors);
}
VisualHistogram::PixelColorHistogram* VisualImage::getHistogram() {
return this->histogram;
}
#if TARGET_OS_MAC
OSStatus VisualImage::createHistogramOfRGBPixelsThread(void* visualImageHistogramPixelColors) {
OSStatus retVal = noErr;
#endif
#if TARGET_OS_WIN
DWORD VisualImage::createHistogramOfRGBPixelsThread(LPVOID visualImageHistogramPixelColors) {
DWORD retVal = 0;
#endif
VisualHistogram::PixelColorHistogram aHistogram = VisualHistogram::createHistogramOfRGBPixels(((VisualImageHistogramPixelColors*)visualImageHistogramPixelColors)->pixelColorValuesVector);
VisualImage* image = ((VisualImageHistogramPixelColors*)visualImageHistogramPixelColors)->visualImage;
if (image->histogram != NULL) {
delete image->histogram;
image->histogram = NULL;
}
if (aHistogram.size() > 0) {
image->histogram = new VisualHistogram::PixelColorHistogram(aHistogram);
}
delete (VisualImageHistogramPixelColors*)visualImageHistogramPixelColors;
if (image->histogram != NULL) {
VisualNotification aNotification;
aNotification.setPointer(image);
aNotification.setKey(kImageHistogramCompletedMsg);
aNotification.post();
}
return retVal;
}
void VisualImage::getRGBHistogramInputPixels(std::vector<PixelColor>& inputValues) const {
VisualImage* histogramImage = this->cloneIndependently();
uint32 maxEdgeLength = 32;
PixelRect pixelRect;
if (this->getWidth() > this->getHeight()) {
pixelRect.height = maxEdgeLength;
pixelRect.width = (uint32)((double)pixelRect.height * ((double)this->getWidth() / (double)this->getHeight()));
} else if (this->getWidth() < this->getHeight()) {
pixelRect.width = maxEdgeLength;
pixelRect.height = (uint32)((double)pixelRect.width * ((double)this->getHeight() / (double)this->getWidth()));
} else {
pixelRect.width = maxEdgeLength;
pixelRect.height = maxEdgeLength;
}
histogramImage->resample(pixelRect);
VisualTextureContainer* histogramTextureContainer = histogramImage->getTextureContainer();
PixelColor* pixels = histogramTextureContainer->createARGBImagePixels();
uint32 numberOfValues = histogramTextureContainer->getImageWidth() * histogramTextureContainer->getImageHeight();
uint8 r, g, b, a;
PixelColor posterizedPixelColor;
for (uint32 i = 0; i < numberOfValues; i++) {
VisualColorTools::convertARGBPixelToRGB(pixels[i]);
VisualColorTools::getColorComponentValues(pixels[i], a, r, g, b);
r = r & 0xfc;
g = g & 0xfc;
b = b & 0xfc;
posterizedPixelColor = VisualColorTools::getPixelColor(a, r, g, b);
inputValues.push_back(posterizedPixelColor);
}
free(pixels);
delete histogramImage;
}
void VisualImage::copy(const VisualImage& other) {
this->visualTextureContainer = new VisualTextureContainer(*(other.visualTextureContainer));
this->isSet = other.isSet;
this->blendMode = other.blendMode;
if (other.histogram != NULL) {
this->histogram = new VisualHistogram::PixelColorHistogram(*(other.histogram));
} else {
this->histogram = NULL;
}
}