1 /*
2  * Project: VizKit
3  * Version: 2.3
4 
5  * Date: 20090823
6  * File: VisualImage.cpp
7  *
8  */
9 
10 /***************************************************************************
11 
12  Copyright (c) 2004-2009 Heiko Wichmann (http://www.imagomat.de/vizkit)
13 
14 
15  This software is provided 'as-is', without any expressed or implied warranty.
16  In no event will the authors be held liable for any damages
17  arising from the use of this software.
18 
19  Permission is granted to anyone to use this software for any purpose,
20  including commercial applications, and to alter it and redistribute it
21  freely, subject to the following restrictions:
22 
23  1. The origin of this software must not be misrepresented;
24  you must not claim that you wrote the original software.
25  If you use this software in a product, an acknowledgment
26  in the product documentation would be appreciated
27  but is not required.
28 
29  2. Altered source versions must be plainly marked as such,
30  and must not be misrepresented as being the original software.
31 
32  3. This notice may not be removed or altered from any source distribution.
33 
34  ***************************************************************************/
35 
36 #include "VisualImage.h"
37 #include "VisualTextureContainer.h"
38 #include "VisualStyledString.h"
39 #include "VisualFile.h"
40 #include "VisualGraphics.h"
41 #include "VisualErrorHandling.h"
42 #include "VisualNotification.h"
43 #include "VisualNetwork.h"
44 #include "VisualDispatch.h"
45 #include "VisualColorTools.h"
46 
47 #if TARGET_OS_WIN
48 #include <windows.h>
49 #include <gdiplus.h>
50 #include <stdio.h>
51 #endif
52 
53 
54 using namespace VizKit;
55 
56 
57 
VisualImage()58 VisualImage::VisualImage() {
59 	visualTextureContainer = new VisualTextureContainer;
60 	isSet = false;
61 	blendMode = kBlend;
62 	histogram = NULL;
63 }
64 
65 
~VisualImage()66 VisualImage::~VisualImage() {
67 	delete visualTextureContainer;
68 	if (histogram != NULL) {
69 		delete histogram;
70 	}
71 }
72 
73 
VisualImage(const VisualImage & other)74 VisualImage::VisualImage(const VisualImage& other) : VisualObject(other) {
75 	copy(other);
76 }
77 
78 
operator =(const VisualImage & other)79 VisualImage& VisualImage::operator=(const VisualImage& other) {
80 
81 	if (this == &other) return *this;
82 
83 	VisualObject::operator=(other);
84 
85 	delete this->visualTextureContainer;
86 
87 	if (this->histogram != NULL) {
88 		delete this->histogram;
89 	}
90 
91 	this->copy(other);
92 
93 	return *this;
94 }
95 
96 
clone(void) const97 VisualImage* VisualImage::clone(void) const {
98 	return new VisualImage(*this);
99 }
100 
101 
cloneIndependently() const102 VisualImage* VisualImage::cloneIndependently() const {
103 	VisualTextureContainer* textureContainer = this->getTextureContainer();
104 	PixelColor* pixels = textureContainer->createARGBImagePixels();
105 	VisualImage* imageCopy = VisualImage::createWithARGBPixelData(pixels, textureContainer->getImageWidth(), textureContainer->getImageHeight());
106 	free(pixels);
107 	return imageCopy;
108 }
109 
110 
initWithFile(VisualFile & aFile)111 bool VisualImage::initWithFile(VisualFile& aFile) {
112 	bool success = this->visualTextureContainer->initWithFile(aFile);
113 	if (success) {
114 		this->isSet = true;
115 	}
116 	return success;
117 }
118 
119 
initWithARGBPixelData(PixelColor * argbPixels,uint32 width,uint32 height)120 bool VisualImage::initWithARGBPixelData(PixelColor* argbPixels, uint32 width, uint32 height) {
121 	bool success = true;
122 	success = this->visualTextureContainer->initWithARGBPixelData(argbPixels, width, height);
123 	if (success) {
124 		this->isSet = true;
125 	}
126 	return success;
127 }
128 
129 
initWithEncodedData(const char * const bufferData,size_t size)130 bool VisualImage::initWithEncodedData(const char* const bufferData, size_t size) {
131 	bool success = this->visualTextureContainer->initWithEncodedData(bufferData, size);
132 	if (success) {
133 		this->isSet = true;
134 	}
135 	return success;
136 }
137 
138 
initWithStyledString(VisualStyledString & styledString)139 bool VisualImage::initWithStyledString(VisualStyledString& styledString) {
140 	bool success = this->visualTextureContainer->initWithStyledString(styledString);
141 	if (success) {
142 		this->isSet = true;
143 	}
144 	return success;
145 }
146 
147 
148 #if TARGET_OS_WIN
initWithResource(int nameId)149 bool VisualImage::initWithResource(int nameId) {
150 	bool success = true;
151 	char* type = "PNG";
152 	char* pngImageData = NULL;
153 	uint32 sizeOfImageResource = 0;
154 	success = VisualFile::getDataOfResource(nameId, type, (void**)&pngImageData, sizeOfImageResource);
155 	if (success) {
156 		success = this->visualTextureContainer->initWithEncodedData((const char* const)pngImageData, sizeOfImageResource);
157 		BOOL deleteSuccess = DeleteObject(pngImageData);
158 	} else {
159 		char errLog[256];
160 		sprintf(errLog, "Err: Unable to get resource data %s in file: %s (line: %d) [%s])", __FILE__, __LINE__, __FUNCTION__);
161 		writeLog(errLog);
162 	}
163 	if (success) {
164 		this->isSet = true;
165 	}
166 	return success;
167 }
168 #endif
169 #if TARGET_OS_MAC
initWithResource(const char * name)170 bool VisualImage::initWithResource(const char* name) {
171 	bool success = true;
172 	VisualFile* resourceFile = VisualFile::createWithResourcesDirectory();
173 	VisualString resourceFileName = VisualString(name);
174 	success = resourceFile->appendFileName(resourceFileName);
175 	if (success) {
176 		success = this->initWithFile(*resourceFile);
177 		delete(resourceFile);
178 		if (success) {
179 			this->isSet = true;
180 		} else {
181 			char errLog[256];
182 			sprintf(errLog, "Err: Unable to load resource %s in file: %s (line: %d) [%s])", name, __FILE__, __LINE__, __FUNCTION__);
183 			writeLog(errLog);
184 		}
185 	}
186 	return success;
187 }
188 #endif
189 
190 
initWithURL(VisualString & anURL,VisualItemIdentifier & anId)191 bool VisualImage::initWithURL(VisualString& anURL, VisualItemIdentifier& anId) {
192 	this->visualTextureContainer->clean();
193 	bool success = VisualNetwork::addToDownloadQueue(anURL, this, anId);
194 	return success;
195 }
196 
197 
initWithLoadedEncodedData()198 bool VisualImage::initWithLoadedEncodedData() {
199 	bool success = false;
200 	if (this->hasData()) {
201 		success = this->visualTextureContainer->initWithEncodedData((const char*)this->getData(), this->getDataSize());
202 	}
203 	this->freeData();
204 	return success;
205 }
206 
207 
initWithFramebuffer(const BottomLeftPositionedPixelRect & clipRect)208 bool VisualImage::initWithFramebuffer(const BottomLeftPositionedPixelRect& clipRect) {
209 	this->visualTextureContainer->clean();
210 	bool success = this->visualTextureContainer->initWithFramebuffer(clipRect);
211 	return success;
212 }
213 
214 
writeToPNGFileAsync(VisualFile & aVisualFile)215 void VisualImage::writeToPNGFileAsync(VisualFile& aVisualFile) {
216 
217 	// Only in the main thread we can transfer the memory of the graphics card to the CPU memory
218 	VisualNotification aNotification;
219 	aNotification.setPointer(this);
220 	aNotification.setObject(aVisualFile);
221 	aNotification.setKey(kImageWriteToPNGFileMsg);
222 	aNotification.post();
223 
224 }
225 
226 
writeToPNGFileAsyncAndDelete(VisualFile & aVisualFile)227 void VisualImage::writeToPNGFileAsyncAndDelete(VisualFile& aVisualFile) {
228 
229 	// Only in the main thread we can transfer the memory of the graphics card to the CPU memory
230 	VisualNotification aNotification;
231 	aNotification.setPointer(this);
232 	aNotification.setObject(aVisualFile);
233 	aNotification.setKey(kImageWriteToPNGFileAndDeleteMsg);
234 	aNotification.post();
235 
236 }
237 
238 
writeToPNGFile(VisualFile & aVisualFile) const239 bool VisualImage::writeToPNGFile(VisualFile& aVisualFile) const {
240 
241 	bool success = true;
242 
243 	PixelColor* texturePixels = this->visualTextureContainer->createARGBImagePixels();
244 
245 	uint32 imageSize = this->visualTextureContainer->getImageWidth() * this->visualTextureContainer->getImageHeight() * sizeof(PixelColor);
246 	PixelColor* flippedImagePixels = (PixelColor*)malloc(imageSize);
247 	for (uint32 i = 0; i < this->visualTextureContainer->getImageHeight(); i++) {
248 		memcpy((flippedImagePixels + this->visualTextureContainer->getImageWidth() * (this->visualTextureContainer->getImageHeight() - i - 1)), (texturePixels + this->visualTextureContainer->getImageWidth() * i), this->visualTextureContainer->getImageWidth() * 4);
249 	}
250 	free(texturePixels);
251 	texturePixels = NULL;
252 
253 #if TARGET_OS_MAC
254 
255 	CFDictionaryRef options = NULL;
256 	CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, flippedImagePixels, imageSize, NULL);
257 	CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
258 	CGBitmapInfo bitmapInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
259 	CGImageRef imageRef = CGImageCreate(this->visualTextureContainer->getImageWidth(), this->visualTextureContainer->getImageHeight(), 8, 32, this->visualTextureContainer->getImageWidth() * 4, colorspace, bitmapInfo, dataProvider, NULL, false, kCGRenderingIntentDefault);
260 
261 	CFMutableDataRef destinationImageData = CFDataCreateMutable(kCFAllocatorDefault, 0);
262 	size_t numberOfImages = 1;
263 	CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)destinationImageData, CFSTR("public.png"), numberOfImages, NULL);
264 
265 	CGImageDestinationAddImage(destination, imageRef, options);
266 
267 	CGImageDestinationFinalize(destination);
268 
269 	CGColorSpaceRelease(colorspace);
270 
271 	const UInt8* dataPointer = CFDataGetBytePtr(destinationImageData);
272 	void** dataHandle = (void**)&dataPointer;
273 	VisualFile::writeDataToFile(dataHandle, CFDataGetLength(destinationImageData), aVisualFile);
274 
275 #endif
276 
277 #if TARGET_OS_WIN
278 
279 	INT stride = sizeof(PixelColor) * this->visualTextureContainer->getImageWidth();
280 
281 	Gdiplus::Bitmap image(this->visualTextureContainer->getImageWidth(),
282 			this->visualTextureContainer->getImageHeight(),
283 			stride,
284 			PixelFormat32bppARGB,
285 			(BYTE*)flippedImagePixels);
286 
287 	IStream* pIStream = NULL;
288 	HRESULT result = CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&pIStream);
289 	if (result != S_OK)
290 		return false;
291 
292 	CLSID pngClsid;
293 	VisualGraphics::getGdiplusEncoderClsid(L"image/png", &pngClsid);
294 
295 	Gdiplus::EncoderParameters encoderParameters;
296 	encoderParameters.Count = 1;
297 	encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
298 	encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
299 	encoderParameters.Parameter[0].NumberOfValues = 1;
300 
301 	// setup compression level
302 
303 	ULONG quality = 50;
304 	encoderParameters.Parameter[0].Value = &quality;
305 
306 	Gdiplus::Status saveStatus = image.Save(pIStream, &pngClsid, &encoderParameters);
307 	if (saveStatus != Gdiplus::Ok) {
308 		pIStream->Release();
309 		return false;
310 	}
311 
312 	// get the size of the stream
313 	ULARGE_INTEGER ulnSize;
314 	LARGE_INTEGER lnOffset;
315 	lnOffset.QuadPart = 0;
316 	if (pIStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize) != S_OK) {
317 		pIStream->Release();
318 		return false;
319 	}
320 
321 	// now move the pointer to the beginning of the file
322 	if (pIStream->Seek(lnOffset, STREAM_SEEK_SET, NULL) != S_OK) {
323 		pIStream->Release();
324 		return false;
325 	}
326 
327 	HGLOBAL hg;
328 	if (GetHGlobalFromStream(pIStream, &hg) == S_OK) {
329 
330 		char* dataPointer = new char[(unsigned int)ulnSize.QuadPart];
331 
332         // Read the stream directly into the buffer
333         ULONG ulBytesRead;
334         if (pIStream->Read(dataPointer, (ULONG)ulnSize.QuadPart, &ulBytesRead) != S_OK) {
335             pIStream->Release();
336             return false;
337         }
338 
339 		void** dataHandle = (void**)&dataPointer;
340 		VisualFile::writeDataToFile(dataHandle, ulBytesRead, aVisualFile);
341 
342 		delete[] dataPointer;
343 	}
344 
345 #endif
346 
347 	free(flippedImagePixels);
348 
349 	return success;
350 
351 }
352 
353 
applyConvolutionFilter(const VisualConvolutionFilter & aConvolutionFilter)354 void VisualImage::applyConvolutionFilter(const VisualConvolutionFilter& aConvolutionFilter) {
355 	this->visualTextureContainer->applyConvolutionFilter(aConvolutionFilter);
356 }
357 
358 
getWidth() const359 uint32 VisualImage::getWidth() const {
360 	return this->visualTextureContainer->getImageWidth();
361 }
362 
363 
getHeight() const364 uint32 VisualImage::getHeight() const {
365 	return this->visualTextureContainer->getImageHeight();
366 }
367 
368 
getLogicalWidth() const369 double VisualImage::getLogicalWidth() const {
370 	return this->visualTextureContainer->getTextureLogicalWidth();
371 }
372 
373 
getLogicalHeight() const374 double VisualImage::getLogicalHeight() const {
375 	return this->visualTextureContainer->getTextureLogicalHeight();
376 }
377 
378 
draw(const VertexChain * const aVertexChain,bool debug)379 void VisualImage::draw(const VertexChain* const aVertexChain, bool debug) {
380 	VisualGraphics* theVisualGraphics = VisualGraphics::getInstance();
381 	theVisualGraphics->drawTexture(this->visualTextureContainer->getTextureName(), aVertexChain, this->visualTextureContainer->getUseRectExtension(), this->blendMode, debug);
382 }
383 
384 
isEmpty() const385 bool VisualImage::isEmpty() const {
386 	return !(this->isSet);
387 }
388 
389 
getBlendMode(void) const390 BlendMode VisualImage::getBlendMode(void) const {
391 	return this->blendMode;
392 }
393 
394 
setBlendMode(BlendMode aBlendMode)395 void VisualImage::setBlendMode(BlendMode aBlendMode) {
396 	this->blendMode = aBlendMode;
397 }
398 
399 
createWithFile(VisualFile & aFile)400 VisualImage* VisualImage::createWithFile(VisualFile& aFile) {
401 	VisualImage* anImage = new VisualImage;
402 	bool success = anImage->initWithFile(aFile);
403 	if (!success) {
404 		delete anImage;
405 		anImage = NULL;
406 	}
407 	return anImage;
408 }
409 
410 
createWithARGBPixelData(PixelColor * argbPixels,uint32 width,uint32 height)411 VisualImage* VisualImage::createWithARGBPixelData(PixelColor* argbPixels, uint32 width, uint32 height) {
412 	VisualImage* anImage = new VisualImage;
413 	bool success = anImage->initWithARGBPixelData(argbPixels, width, height);
414 	if (!success) {
415 		delete anImage;
416 		anImage = NULL;
417 	}
418 	return anImage;
419 }
420 
421 
createWithEncodedData(const char * const bufferData,uint32 size)422 VisualImage* VisualImage::createWithEncodedData(const char* const bufferData, uint32 size) {
423 	VisualImage* anImage = new VisualImage;
424 	bool success = anImage->initWithEncodedData(bufferData, size);
425 	if (!success) {
426 		delete anImage;
427 		anImage = NULL;
428 	}
429 	return anImage;
430 }
431 
432 
createWithStyledString(VisualStyledString & styledString)433 VisualImage* VisualImage::createWithStyledString(VisualStyledString& styledString) {
434 	VisualImage* anImage = new VisualImage;
435 	bool success = anImage->initWithStyledString(styledString);
436 	if (!success) {
437 		delete anImage;
438 		anImage = NULL;
439 	}
440 	return anImage;
441 }
442 
443 
444 #if TARGET_OS_MAC
createWithResource(const char * name)445 VisualImage* VisualImage::createWithResource(const char* name) {
446 #endif
447 #if TARGET_OS_WIN
448 VisualImage* VisualImage::createWithResource(int nameId) {
449 #endif
450 	VisualImage* anImage = new VisualImage;
451 #if TARGET_OS_MAC
452 	bool success = anImage->initWithResource(name);
453 #endif
454 #if TARGET_OS_WIN
455 	bool success = anImage->initWithResource(nameId);
456 #endif
457 	if (!success) {
458 		delete anImage;
459 		anImage = NULL;
460 	}
461 	return anImage;
462 }
463 
464 
465 VisualImage* VisualImage::createWithURL(VisualString& anURL, VisualItemIdentifier& anId) {
466 	VisualImage* anImage = new VisualImage;
467 	bool success = anImage->initWithURL(anURL, anId);
468 	if (!success) {
469 		delete anImage;
470 		anImage = NULL;
471 	}
472 	return anImage;
473 }
474 
475 
476 VisualImage* VisualImage::createWithFramebuffer(const BottomLeftPositionedPixelRect& clipRect) {
477 	VisualImage* anImage = new VisualImage;
478 	bool success = anImage->initWithFramebuffer(clipRect);
479 	if (!success) {
480 		delete anImage;
481 		anImage = NULL;
482 	}
483 	return anImage;
484 }
485 
486 
487 void VisualImage::resample(const PixelRect& pixelRect) {
488 	this->visualTextureContainer->resample(pixelRect);
489 }
490 
491 
492 VisualTextureContainer* VisualImage::getTextureContainer() const {
493 	return this->visualTextureContainer;
494 }
495 
496 
497 void VisualImage::dataLoadDidEnd(const VisualItemIdentifier& identifier) {
498 	// Only in the main thread we can transfer the fetched encoded image to the memory of the graphics card
499 	// (OpenGL calls must be executed in the main thread)
500 	VisualNotification aNotification(identifier);
501 	aNotification.setPointer(this);
502 	aNotification.setKey(kLoadingEncodedImageDataCompletedMsg);
503 	aNotification.post();
504 }
505 
506 
507 void VisualImage::createHistogram() {
508 
509 	VisualImageHistogramPixelColors* visualImageHistogramPixelColors = new VisualImageHistogramPixelColors;
510 	visualImageHistogramPixelColors->visualImage = this;
511 
512 	this->getRGBHistogramInputPixels(visualImageHistogramPixelColors->pixelColorValuesVector);
513 
514 	bool success = false;
515 	success = VisualThreading::createThread((ThreadingFuncPtr)VisualImage::createHistogramOfRGBPixelsThread, (void*)visualImageHistogramPixelColors);
516 
517 }
518 
519 
520 VisualHistogram::PixelColorHistogram* VisualImage::getHistogram() {
521 	return this->histogram;
522 }
523 
524 
525 #if TARGET_OS_MAC
526 OSStatus VisualImage::createHistogramOfRGBPixelsThread(void* visualImageHistogramPixelColors) {
527 	OSStatus retVal = noErr;
528 #endif
529 #if TARGET_OS_WIN
530 DWORD VisualImage::createHistogramOfRGBPixelsThread(LPVOID visualImageHistogramPixelColors) {
531 	DWORD retVal = 0;
532 #endif
533 
534 	VisualHistogram::PixelColorHistogram aHistogram = VisualHistogram::createHistogramOfRGBPixels(((VisualImageHistogramPixelColors*)visualImageHistogramPixelColors)->pixelColorValuesVector);
535 
536 	VisualImage* image = ((VisualImageHistogramPixelColors*)visualImageHistogramPixelColors)->visualImage;
537 	if (image->histogram != NULL) {
538 		delete image->histogram;
539 		image->histogram = NULL;
540 	}
541 	if (aHistogram.size() > 0) {
542 		image->histogram = new VisualHistogram::PixelColorHistogram(aHistogram);
543 	}
544 
545 	delete (VisualImageHistogramPixelColors*)visualImageHistogramPixelColors;
546 
547 	if (image->histogram != NULL) {
548 		VisualNotification aNotification;
549 		aNotification.setPointer(image);
550 		aNotification.setKey(kImageHistogramCompletedMsg);
551 		aNotification.post();
552 	}
553 
554 	return retVal;
555 }
556 
557 
558 void VisualImage::getRGBHistogramInputPixels(std::vector<PixelColor>& inputValues) const {
559 
560 	VisualImage* histogramImage = this->cloneIndependently();
561 
562 	uint32 maxEdgeLength = 32;
563 	PixelRect pixelRect;
564 	if (this->getWidth() > this->getHeight()) {
565 		pixelRect.height = maxEdgeLength;
566 		pixelRect.width = (uint32)((double)pixelRect.height * ((double)this->getWidth() / (double)this->getHeight()));
567 	} else if (this->getWidth() < this->getHeight()) {
568 		pixelRect.width = maxEdgeLength;
569 		pixelRect.height = (uint32)((double)pixelRect.width * ((double)this->getHeight() / (double)this->getWidth()));
570 	} else {
571 		pixelRect.width = maxEdgeLength;
572 		pixelRect.height = maxEdgeLength;
573 	}
574 
575 	histogramImage->resample(pixelRect);
576 
577 	VisualTextureContainer* histogramTextureContainer = histogramImage->getTextureContainer();
578 
579 	PixelColor* pixels = histogramTextureContainer->createARGBImagePixels();
580 
581 	uint32 numberOfValues = histogramTextureContainer->getImageWidth() * histogramTextureContainer->getImageHeight();
582 
583 	uint8 r, g, b, a;
584 	PixelColor posterizedPixelColor;
585 	for (uint32 i = 0; i < numberOfValues; i++) {
586 		VisualColorTools::convertARGBPixelToRGB(pixels[i]);
587 		VisualColorTools::getColorComponentValues(pixels[i], a, r, g, b);
588 		r = r & 0xfc;
589 		g = g & 0xfc;
590 		b = b & 0xfc;
591 		posterizedPixelColor = VisualColorTools::getPixelColor(a, r, g, b);
592 		inputValues.push_back(posterizedPixelColor);
593 	}
594 	free(pixels);
595 
596 	delete histogramImage;
597 
598 }
599 
600 
601 void VisualImage::copy(const VisualImage& other) {
602 	this->visualTextureContainer = new VisualTextureContainer(*(other.visualTextureContainer));
603 	this->isSet = other.isSet;
604 	this->blendMode = other.blendMode;
605 	if (other.histogram != NULL) {
606 		this->histogram = new VisualHistogram::PixelColorHistogram(*(other.histogram));
607 	} else {
608 		this->histogram = NULL;
609 	}
610 }
611