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