4 |
|
|
5 |
//--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- |
6 |
|
|
7 |
void on_loader_frame_done(GdkPixbufLoader *loader, gpointer arg1, |
void on_loader_area_prepared(GdkPixbufLoader *loader, gpointer arg1, |
8 |
gpointer user_data) { |
gpointer user_data) { |
9 |
|
|
10 |
ImageCache *imageCache = |
((CachedImage *)user_data)->loaderPrepared(); |
|
(ImageCache *)gtk_object_get_user_data(GTK_OBJECT(loader)); |
|
|
imageCache->loaderImageDone((CachedURX *)user_data); |
|
11 |
} |
} |
12 |
|
|
13 |
//--------------------------------------------------------------------------- |
void on_loader_frame_done(GdkPixbufLoader *loader, gpointer arg1, |
14 |
|
gpointer user_data) { |
|
CachedURX::CachedURX() { |
|
|
loader = NULL; |
|
|
} |
|
|
|
|
|
CachedURX::~CachedURX() { |
|
|
if (loader != NULL) delete loader; |
|
|
} |
|
15 |
|
|
16 |
CachedLOD *CachedURX::getCachedLOD(int lod) { |
((CachedImage *)user_data)->loaderDone(); |
|
return cachedLODs[lod]; |
|
17 |
} |
} |
18 |
|
|
19 |
GdkPixbuf *CachedURX::getLoaderPixbuf() { |
//--------------------------------------------------------------------------- |
|
return (loader == NULL ? NULL : gdk_pixbuf_loader_get_pixbuf(loader)); |
|
|
} |
|
20 |
|
|
21 |
void CachedURX::setCachedLOD(int lod, CachedLOD *cl) { |
#define START_PIXBUF_BACKGROUND_LOADER ((GdkPixbuf *)(-1)) |
|
if (cl == NULL) cachedLODs.erase(lod); |
|
|
else cachedLODs[lod] = cl; |
|
|
} |
|
22 |
|
|
23 |
void CachedURX::write(char *data, int len, ImageCache *imageCache) { |
CachedImage::CachedImage() { |
24 |
|
loader = NULL; |
25 |
|
requestedLOD = -1; |
26 |
|
pixbuf = NULL; |
27 |
|
pixbufLOD = -1; |
28 |
|
lastSize = 0; |
29 |
|
currentSize = 0; |
30 |
|
} |
31 |
|
|
32 |
|
CachedImage::~CachedImage() { |
33 |
|
closeLoader(); |
34 |
|
if (pixbuf != NULL) gdk_pixbuf_unref(pixbuf); |
35 |
|
} |
36 |
|
|
37 |
|
GdkPixbuf *CachedImage::getPixbuf(int lod) { |
38 |
|
if (lod < 0) lod = 0; |
39 |
|
if ((pixbuf != NULL) && (lod >= pixbufLOD)) { |
40 |
|
return pixbuf; |
41 |
|
} |
42 |
if (loader == NULL) { |
if (loader == NULL) { |
43 |
|
requestedLOD = lod; |
44 |
loader = gdk_pixbuf_loader_new(); |
loader = gdk_pixbuf_loader_new(); |
45 |
gtk_object_set_user_data(GTK_OBJECT(loader), imageCache); |
if (loader == NULL) return NULL; |
46 |
gtk_signal_connect(GTK_OBJECT(loader), "frame-done", |
gtk_signal_connect(GTK_OBJECT(loader), "frame-done", |
47 |
GTK_SIGNAL_FUNC(on_loader_frame_done), this); |
GTK_SIGNAL_FUNC(on_loader_frame_done), this); |
48 |
} |
gtk_signal_connect(GTK_OBJECT(loader), "area-prepared", |
49 |
gdk_pixbuf_loader_write(loader, (guchar *)data, len); |
GTK_SIGNAL_FUNC(on_loader_area_prepared), this); |
|
} |
|
50 |
|
|
51 |
void CachedURX::closeLoader() { |
return START_PIXBUF_BACKGROUND_LOADER; |
|
if (loader != NULL) { |
|
|
gdk_pixbuf_loader_close(loader); |
|
|
loader = NULL; |
|
52 |
} |
} |
53 |
|
if (lod < requestedLOD) requestedLOD = lod; |
54 |
|
|
55 |
|
GdkPixbuf *pb = gdk_pixbuf_loader_get_pixbuf(loader); |
56 |
|
return (pb == NULL ? pixbuf : pb); |
57 |
} |
} |
58 |
|
|
59 |
//--------------------------------------------------------------------------- |
int CachedImage::write(char *data, int len) { |
60 |
|
if (loader == NULL) return -1; |
61 |
|
gdk_pixbuf_loader_write(loader, (guchar *)data, len); |
62 |
|
return 0; |
63 |
|
} |
64 |
|
|
65 |
void CachedLOD::createScaled(GdkPixbuf *from, double scale) { |
void CachedImage::dropLOD() { |
66 |
int orig_width = gdk_pixbuf_get_width (from); |
closeLoader(); |
67 |
int orig_height = gdk_pixbuf_get_height(from); |
if (pixbuf == NULL) return; |
68 |
|
|
69 |
int width = (int)(scale * orig_width ); if (width < 1) width = 1; |
int dest_width = gdk_pixbuf_get_width (pixbuf) >> 1; |
70 |
int height = (int)(scale * orig_height); if (height < 1) height = 1; |
int dest_height = gdk_pixbuf_get_height(pixbuf) >> 1; |
71 |
|
if (dest_width == 0) dest_width = 1; |
72 |
|
if (dest_height == 0) dest_height = 1; |
73 |
|
|
74 |
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height); |
GdkPixbuf *dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, |
75 |
gdk_pixbuf_ref(pixbuf); |
dest_width, dest_height); |
76 |
|
|
77 |
gdk_pixbuf_scale(from, pixbuf, 0, 0, width, height, 0, 0, |
gdk_pixbuf_scale(pixbuf, dest, 0, 0, dest_width, dest_height, 0, 0, |
78 |
scale, scale, GDK_INTERP_BILINEAR); |
0.5, 0.5, GDK_INTERP_BILINEAR); |
79 |
|
|
80 |
size = width*height*4; |
gdk_pixbuf_unref(pixbuf); |
81 |
} |
pixbuf = dest; |
82 |
|
gdk_pixbuf_ref(pixbuf); |
|
CachedLOD::CachedLOD(int newLOD, CachedURX *cu, gboolean final) { |
|
|
next = NULL; prev = NULL; |
|
|
|
|
|
lod = newLOD; |
|
|
cachedURX = cu; |
|
83 |
|
|
84 |
createScaled(cu->getLoaderPixbuf(), 1.0 / (1 << lod)); |
--pixbufLOD; |
85 |
|
|
86 |
fullyLoaded = final; |
currentSize = dest_width * dest_height * 4; |
87 |
cachedURX->setCachedLOD(lod, this); |
gtk_object_set_user_data(GTK_OBJECT(pixbuf), (gpointer)pixbufLOD); |
88 |
} |
} |
89 |
|
|
90 |
CachedLOD::CachedLOD(int newLOD, CachedLOD *cl) { |
void CachedImage::loaderPrepared() { |
91 |
next = NULL; prev = NULL; |
if ((pixbuf == NULL) || (loader == NULL)) return; |
|
|
|
|
cachedURX = cl->getCachedURX(); |
|
92 |
|
|
93 |
lod = newLOD; |
GdkPixbuf *dest = gdk_pixbuf_loader_get_pixbuf(loader); |
|
int orig_lod = cl->getLOD(); |
|
94 |
|
|
95 |
int lod_dif = orig_lod - lod; |
double scale = 1 << pixbufLOD; |
96 |
|
|
97 |
if (lod_dif >= 0) { |
gdk_pixbuf_scale(pixbuf, dest, 0, 0, |
98 |
createScaled(cl->pixbuf, 1.0 / (1 << lod_dif)); |
gdk_pixbuf_get_width(dest), gdk_pixbuf_get_height(dest), |
99 |
fullyLoaded = cl->isFullyLoaded(); |
0, 0, scale, scale, GDK_INTERP_BILINEAR); |
|
} |
|
|
else { |
|
|
createScaled(cl->pixbuf, 1 << (-lod_dif)); |
|
|
fullyLoaded = false; |
|
|
} |
|
|
cachedURX->setCachedLOD(lod, this); |
|
|
} |
|
100 |
|
|
101 |
CachedLOD::~CachedLOD() { |
gtk_object_set_user_data(GTK_OBJECT(dest), (gpointer)0); |
|
cachedURX->setCachedLOD(lod, NULL); |
|
|
gdk_pixbuf_unref(pixbuf); |
|
102 |
} |
} |
103 |
|
|
104 |
CachedLOD *CachedLOD::getNext() { return next; } |
void CachedImage::loaderDone() { |
105 |
CachedLOD *CachedLOD::getPrev() { return prev; } |
if (loader == NULL) return; |
106 |
|
if (pixbuf != NULL) gdk_pixbuf_unref(pixbuf); |
107 |
|
|
108 |
void CachedLOD::setNext(CachedLOD *cl) { next = cl; } |
GdkPixbuf *src = gdk_pixbuf_loader_get_pixbuf(loader); |
109 |
void CachedLOD::setPrev(CachedLOD *cl) { prev = cl; } |
int dest_width = gdk_pixbuf_get_width (src) >> requestedLOD; |
110 |
|
int dest_height = gdk_pixbuf_get_height(src) >> requestedLOD; |
111 |
|
if (dest_width == 0) dest_width = 1; |
112 |
|
if (dest_height == 0) dest_height = 1; |
113 |
|
double scale = 1.0 / (1 << requestedLOD); |
114 |
|
|
115 |
GdkPixbuf *CachedLOD::getPixbuf() { return pixbuf; } |
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, |
116 |
|
dest_width, dest_height); |
117 |
|
gdk_pixbuf_ref(pixbuf); |
118 |
|
|
119 |
int CachedLOD::getSize() { return size; } |
gdk_pixbuf_scale(src, pixbuf, 0, 0, dest_width, dest_height, 0, 0, |
120 |
CachedURX *CachedLOD::getCachedURX() { return cachedURX; } |
scale, scale, GDK_INTERP_BILINEAR); |
121 |
int CachedLOD::getLOD() { return lod; } |
pixbufLOD = requestedLOD; |
|
gboolean CachedLOD::isFullyLoaded() { return fullyLoaded; } |
|
122 |
|
|
123 |
void CachedLOD::update(GdkPixbuf *from, gboolean final) { |
currentSize = dest_width * dest_height * 4; |
|
if ((pixbuf == NULL) || fullyLoaded) return; |
|
124 |
|
|
125 |
int width = gdk_pixbuf_get_width (pixbuf); |
gtk_object_set_user_data(GTK_OBJECT(pixbuf), (gpointer)pixbufLOD); |
126 |
int height = gdk_pixbuf_get_height(pixbuf); |
closeLoader(); |
127 |
|
} |
128 |
|
|
129 |
double scale = 1.0 / (1 << lod); |
void CachedImage::closeLoader() { |
130 |
|
if (loader != NULL) { |
131 |
|
gdk_pixbuf_loader_close(loader); |
132 |
|
loader = NULL; |
133 |
|
} |
134 |
|
} |
135 |
|
|
136 |
gdk_pixbuf_scale(from, pixbuf, 0, 0, width, height, 0, 0, |
int CachedImage::getSizeChange() { |
137 |
scale, scale, GDK_INTERP_BILINEAR); |
int change = currentSize - lastSize; |
138 |
|
lastSize = currentSize; |
139 |
|
return change; |
140 |
|
} |
141 |
|
|
142 |
fullyLoaded = final; |
int CachedImage::getCurrentSize() { |
143 |
|
return currentSize; |
144 |
} |
} |
145 |
|
|
146 |
//--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- |
147 |
|
|
148 |
ImageCache::ImageCache() { |
ImageCache::ImageCache() { |
|
recentlyUsedLODs = NULL; |
|
149 |
size = 0; |
size = 0; |
150 |
maxSize = 10*1024*1024; |
loading = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, 16, 16); |
151 |
loaderPixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, 16, 16); |
gdk_pixbuf_ref(loading); |
|
gdk_pixbuf_ref(loaderPixbuf); |
|
152 |
} |
} |
153 |
|
|
154 |
ImageCache::~ImageCache() { |
ImageCache::~ImageCache() { |
155 |
CachedURX *cu; |
imageByUrx.clear(); |
156 |
CachedLOD *cl = recentlyUsedLODs; |
urxByImage.clear(); |
157 |
CachedLOD *oldcl; |
|
158 |
while (cl != NULL) { |
list<CachedImage *>::iterator iter; |
|
cu = cl->getCachedURX(); |
|
|
cu->closeLoader(); |
|
|
oldcl = cl; |
|
|
cl = cl->getNext(); |
|
|
delete oldcl; |
|
|
} |
|
|
cachedURXs.clear(); |
|
|
gdk_pixbuf_unref(loaderPixbuf); |
|
|
} |
|
159 |
|
|
160 |
/** |
for (iter = recentlyUsedImages.begin(); |
161 |
* Create new level of detail from another level |
iter != recentlyUsedImages.end(); |
162 |
* and put it on top of the recentlyUsedLODs list. |
++iter) { |
163 |
*/ |
|
164 |
CachedLOD *ImageCache::createCachedLOD(int lod, CachedLOD *oldcl) { |
delete *iter; |
165 |
CachedLOD *cl = new CachedLOD(lod, oldcl); |
} |
166 |
size += cl->getSize(); |
recentlyUsedImages.clear(); |
167 |
cl->setNext(recentlyUsedLODs); |
gdk_pixbuf_unref(loading); |
|
cl->getNext()->setPrev(cl); |
|
|
recentlyUsedLODs = cl; |
|
|
return cl; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Create a new level of detail from a (LOD 0) pixbuf |
|
|
* and put it on top of the recentlyUsedLODs list. |
|
|
*/ |
|
|
CachedLOD *ImageCache::createCachedLOD(int lod, CachedURX *cu, |
|
|
gboolean final) { |
|
|
CachedLOD *cl = new CachedLOD(lod, cu, final); |
|
|
size += cl->getSize(); |
|
|
cl->setNext(recentlyUsedLODs); |
|
|
cl->getNext()->setPrev(cl); |
|
|
recentlyUsedLODs = cl; |
|
|
return cl; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Move a level of detail to the top of the recentlyUsedLODs list. |
|
|
*/ |
|
|
void ImageCache::useCachedLOD(CachedLOD *cl) { |
|
|
if (cl->getNext() != NULL) cl->getNext()->setPrev(cl->getPrev()); |
|
|
if (cl->getPrev() != NULL) cl->getPrev()->setNext(cl->getNext()); |
|
|
cl->setPrev(NULL); |
|
|
cl->setNext(recentlyUsedLODs); |
|
|
cl->getNext()->setPrev(cl); |
|
|
recentlyUsedLODs = cl; |
|
168 |
} |
} |
169 |
|
|
170 |
GdkPixbuf *ImageCache::getPixbuf(string urx, int lod) { |
GdkPixbuf *ImageCache::getPixbuf(string urx, int lod) { |
171 |
CachedLOD *wantedLOD; |
CachedImage *ci = imageByUrx[urx]; |
172 |
CachedLOD *gotLOD; |
if (ci == NULL) { |
173 |
CachedURX *wantedURX = cachedURXs[urx]; |
ci = new CachedImage(); |
174 |
|
imageByUrx[urx] = ci; |
175 |
//----------------------------------------------------------------- |
urxByImage[ci] = urx; |
176 |
// If the wanted URX was not found... |
} |
177 |
//----------------------------------------------------------------- |
else recentlyUsedImages.remove(ci); |
|
if (wantedURX == NULL) { |
|
|
// Start loading! |
|
178 |
|
|
179 |
// backgroundLoader.startLoading(urx); XXXXXXX |
recentlyUsedImages.push_front(ci); |
180 |
|
|
181 |
return loaderPixbuf; |
size += ci->getSizeChange(); |
|
} |
|
|
//----------------------------------------------------------------- |
|
|
// The wanted URX was found, how about the Level of Detail? |
|
|
//----------------------------------------------------------------- |
|
|
wantedLOD = wantedURX->getCachedLOD(lod); |
|
|
//----------------------------------------------------------------- |
|
|
// If the wanted LOD was not found... |
|
|
//----------------------------------------------------------------- |
|
|
if (wantedLOD == NULL) { |
|
|
// See if it can be resized from another LOD |
|
|
// Try to find one that is bigger, and fully loaded... |
|
|
for (int tryLOD = lod-1; tryLOD >= 0; tryLOD--) { |
|
|
gotLOD = wantedURX->getCachedLOD(tryLOD); |
|
|
if ((gotLOD != NULL) && (gotLOD->isFullyLoaded())) { |
|
|
wantedLOD = createCachedLOD(lod, gotLOD); |
|
|
return wantedLOD->getPixbuf(); |
|
|
} |
|
|
} |
|
|
// There was no fully loaded one, so try the pixbufloader: |
|
|
GdkPixbuf *pb = wantedURX->getLoaderPixbuf(); |
|
|
if (pb != NULL) { |
|
|
wantedLOD = createCachedLOD(lod, wantedURX, false); |
|
|
return wantedLOD->getPixbuf(); |
|
|
} |
|
|
// Finally check the few next levels for smaller versions |
|
|
// if one is found, copy it and start loading a better one |
|
|
for (int tryLOD = lod+1; tryLOD < lod+6; tryLOD++) { |
|
|
gotLOD = wantedURX->getCachedLOD(tryLOD); |
|
|
if ((gotLOD != NULL) && (gotLOD->isFullyLoaded())) { |
|
|
wantedLOD = createCachedLOD(lod, gotLOD); |
|
182 |
|
|
183 |
// backgroundLoader.startLoading(urx); XXXXXXX |
GdkPixbuf *pb = ci->getPixbuf(lod); |
184 |
|
if (pb == START_PIXBUF_BACKGROUND_LOADER) { |
185 |
|
|
186 |
return wantedLOD->getPixbuf(); |
//backgroundLoader->startLoading(urx); |
|
} |
|
|
} |
|
|
return loaderPixbuf; |
|
|
} |
|
|
//----------------------------------------------------------------- |
|
|
// If the wanted LOD is not fully loaded: |
|
|
//----------------------------------------------------------------- |
|
|
if (!wantedLOD->isFullyLoaded()) { |
|
|
// Check if it's being loaded in the pixbufloader: |
|
|
GdkPixbuf *pb = wantedURX->getLoaderPixbuf(); |
|
|
if (pb != NULL) { |
|
|
wantedLOD->update(pb, false); |
|
|
useCachedLOD(wantedLOD); |
|
|
return wantedLOD->getPixbuf(); |
|
|
} |
|
|
// See if it can be resized from another LOD: |
|
|
for (int tryLOD = lod-1; tryLOD >= 0; tryLOD--) { |
|
|
gotLOD = wantedURX->getCachedLOD(tryLOD); |
|
|
if ((gotLOD != NULL) && (gotLOD->isFullyLoaded())) { |
|
|
wantedLOD->update(gotLOD->getPixbuf(), true); |
|
|
useCachedLOD(wantedLOD); |
|
|
return wantedLOD->getPixbuf(); |
|
|
} |
|
|
} |
|
|
// The pixbufloader's area has not been prepared yet? |
|
|
return loaderPixbuf; |
|
|
} |
|
|
//----------------------------------------------------------------- |
|
|
// The wanted LOD was fully loaded: |
|
|
//----------------------------------------------------------------- |
|
|
useCachedLOD(wantedLOD); // move to top of recentlyUsedLODs |
|
|
return wantedLOD->getPixbuf(); |
|
|
//----------------------------------------------------------------- |
|
|
} |
|
187 |
|
|
188 |
int ImageCache::getWidth(string urx, int lod) { |
pb = ci->getPixbuf(lod); |
189 |
return gdk_pixbuf_get_width(getPixbuf(urx,lod)); |
} |
190 |
|
return (pb == NULL ? loading : pb); |
191 |
} |
} |
192 |
|
|
193 |
int ImageCache::getHeight(string urx, int lod) { |
int ImageCache::write(string urx, char *data, int len) { |
194 |
return gdk_pixbuf_get_height(getPixbuf(urx,lod)); |
CachedImage *ci = imageByUrx[urx]; |
195 |
|
return (ci == NULL ? -1 : ci->write(data, len)); |
196 |
} |
} |
197 |
|
|
198 |
int *ImageCache::getPixels(string urx, int lod) { |
int ImageCache::cropToSize(int maxSize) { |
199 |
return (int *)gdk_pixbuf_get_pixels(getPixbuf(urx,lod)); |
CachedImage *ci; |
200 |
} |
int sizeChange; |
201 |
|
while ((size >= maxSize) && (!recentlyUsedImages.empty())) { |
202 |
|
ci = recentlyUsedImages.back(); |
203 |
|
recentlyUsedImages.pop_back(); |
204 |
|
|
205 |
void ImageCache::write(string urx, char *data, int len) { |
ci->dropLOD(); |
|
CachedURX *wantedURX = cachedURXs[urx]; |
|
|
if (wantedURX == NULL) { |
|
|
wantedURX = new CachedURX(); |
|
|
cachedURXs[urx] = wantedURX; |
|
|
} |
|
|
wantedURX->write(data, len, this); |
|
|
} |
|
206 |
|
|
207 |
void ImageCache::loaderImageDone(CachedURX *cu) { |
sizeChange = ci->getSizeChange(); |
208 |
CachedLOD *cl = cu->getCachedLOD(0); |
if (sizeChange == 0) { |
209 |
if (cl == NULL) { |
string urx = urxByImage[ci]; |
210 |
cl = createCachedLOD(0, cu, true); |
urxByImage.erase(ci); |
211 |
} |
imageByUrx.erase(urx); |
212 |
else { |
size -= ci->getCurrentSize(); |
213 |
cl->update(cu->getLoaderPixbuf(), true); |
delete ci; |
214 |
useCachedLOD(cl); |
} |
215 |
|
else { |
216 |
|
size += sizeChange; |
217 |
|
recentlyUsedImages.push_front(ci); |
218 |
|
} |
219 |
} |
} |
220 |
cu->closeLoader(); |
return size; |
221 |
} |
} |
222 |
|
|
223 |
//--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- |