47 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
48 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
49 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
50 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
51 // |
52 // |
|
53 //--------------------------------------------------------------------------------- |
|
54 // |
|
55 |
|
56 #include "lcms2_internal.h" |
52 |
57 |
53 // Generic I/O, tag dictionary management, profile struct |
58 // Generic I/O, tag dictionary management, profile struct |
54 |
59 |
55 |
60 // IOhandlers are abstractions used by littleCMS to read from whatever file, stream, |
56 |
61 // memory block or any storage. Each IOhandler provides implementations for read, |
57 #include "lcms.h" |
62 // write, seek and tell functions. LittleCMS code deals with IO across those objects. |
58 |
63 // In this way, is easier to add support for new storage media. |
59 |
64 |
60 // Memory-based stream --------------------------------------------------- |
65 // NULL stream, for taking care of used space ------------------------------------- |
|
66 |
|
67 // NULL IOhandler basically does nothing but keep track on how many bytes have been |
|
68 // written. This is handy when creating profiles, where the file size is needed in the |
|
69 // header. Then, whole profile is serialized across NULL IOhandler and a second pass |
|
70 // writes the bytes to the pertinent IOhandler. |
61 |
71 |
62 typedef struct { |
72 typedef struct { |
63 LPBYTE Block; // Points to allocated memory |
73 cmsUInt32Number Pointer; // Points to current location |
64 size_t Size; // Size of allocated memory |
74 } FILENULL; |
65 size_t Pointer; // Points to current location |
75 |
66 int FreeBlockOnClose; // As title |
76 static |
67 |
77 cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) |
68 } FILEMEM; |
78 { |
69 |
79 FILENULL* ResData = (FILENULL*) iohandler ->stream; |
70 static |
80 |
71 LPVOID MemoryOpen(LPBYTE Block, size_t Size, char Mode) |
81 cmsUInt32Number len = size * count; |
72 { |
82 ResData -> Pointer += len; |
73 FILEMEM* fm = (FILEMEM*) _cmsMalloc(sizeof(FILEMEM)); |
83 return count; |
74 if (fm == NULL) return NULL; |
84 |
75 |
85 cmsUNUSED_PARAMETER(Buffer); |
76 ZeroMemory(fm, sizeof(FILEMEM)); |
86 } |
77 |
87 |
78 if (Mode == 'r') { |
88 static |
79 |
89 cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) |
80 fm ->Block = (LPBYTE) _cmsMalloc(Size); |
90 { |
|
91 FILENULL* ResData = (FILENULL*) iohandler ->stream; |
|
92 |
|
93 ResData ->Pointer = offset; |
|
94 return TRUE; |
|
95 } |
|
96 |
|
97 static |
|
98 cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) |
|
99 { |
|
100 FILENULL* ResData = (FILENULL*) iohandler ->stream; |
|
101 return ResData -> Pointer; |
|
102 } |
|
103 |
|
104 static |
|
105 cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr) |
|
106 { |
|
107 FILENULL* ResData = (FILENULL*) iohandler ->stream; |
|
108 |
|
109 ResData ->Pointer += size; |
|
110 if (ResData ->Pointer > iohandler->UsedSpace) |
|
111 iohandler->UsedSpace = ResData ->Pointer; |
|
112 |
|
113 return TRUE; |
|
114 |
|
115 cmsUNUSED_PARAMETER(Ptr); |
|
116 } |
|
117 |
|
118 static |
|
119 cmsBool NULLClose(cmsIOHANDLER* iohandler) |
|
120 { |
|
121 FILENULL* ResData = (FILENULL*) iohandler ->stream; |
|
122 |
|
123 _cmsFree(iohandler ->ContextID, ResData); |
|
124 _cmsFree(iohandler ->ContextID, iohandler); |
|
125 return TRUE; |
|
126 } |
|
127 |
|
128 // The NULL IOhandler creator |
|
129 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID) |
|
130 { |
|
131 struct _cms_io_handler* iohandler = NULL; |
|
132 FILENULL* fm = NULL; |
|
133 |
|
134 iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler)); |
|
135 if (iohandler == NULL) return NULL; |
|
136 |
|
137 fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL)); |
|
138 if (fm == NULL) goto Error; |
|
139 |
|
140 fm ->Pointer = 0; |
|
141 |
|
142 iohandler ->ContextID = ContextID; |
|
143 iohandler ->stream = (void*) fm; |
|
144 iohandler ->UsedSpace = 0; |
|
145 iohandler ->PhysicalFile[0] = 0; |
|
146 |
|
147 iohandler ->Read = NULLRead; |
|
148 iohandler ->Seek = NULLSeek; |
|
149 iohandler ->Close = NULLClose; |
|
150 iohandler ->Tell = NULLTell; |
|
151 iohandler ->Write = NULLWrite; |
|
152 |
|
153 return iohandler; |
|
154 |
|
155 Error: |
|
156 if (fm) _cmsFree(ContextID, fm); |
|
157 if (iohandler) _cmsFree(ContextID, iohandler); |
|
158 return NULL; |
|
159 |
|
160 } |
|
161 |
|
162 |
|
163 // Memory-based stream -------------------------------------------------------------- |
|
164 |
|
165 // Those functions implements an iohandler which takes a block of memory as storage medium. |
|
166 |
|
167 typedef struct { |
|
168 cmsUInt8Number* Block; // Points to allocated memory |
|
169 cmsUInt32Number Size; // Size of allocated memory |
|
170 cmsUInt32Number Pointer; // Points to current location |
|
171 int FreeBlockOnClose; // As title |
|
172 |
|
173 } FILEMEM; |
|
174 |
|
175 static |
|
176 cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) |
|
177 { |
|
178 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; |
|
179 cmsUInt8Number* Ptr; |
|
180 cmsUInt32Number len = size * count; |
|
181 |
|
182 if (ResData -> Pointer + len > ResData -> Size){ |
|
183 |
|
184 len = (ResData -> Size - ResData -> Pointer); |
|
185 cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size); |
|
186 return 0; |
|
187 } |
|
188 |
|
189 Ptr = ResData -> Block; |
|
190 Ptr += ResData -> Pointer; |
|
191 memmove(Buffer, Ptr, len); |
|
192 ResData -> Pointer += len; |
|
193 |
|
194 return count; |
|
195 } |
|
196 |
|
197 // SEEK_CUR is assumed |
|
198 static |
|
199 cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset) |
|
200 { |
|
201 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; |
|
202 |
|
203 if (offset > ResData ->Size) { |
|
204 cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile"); |
|
205 return FALSE; |
|
206 } |
|
207 |
|
208 ResData ->Pointer = offset; |
|
209 return TRUE; |
|
210 } |
|
211 |
|
212 // Tell for memory |
|
213 static |
|
214 cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler) |
|
215 { |
|
216 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; |
|
217 |
|
218 if (ResData == NULL) return 0; |
|
219 return ResData -> Pointer; |
|
220 } |
|
221 |
|
222 |
|
223 // Writes data to memory, also keeps used space for further reference. |
|
224 static |
|
225 cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr) |
|
226 { |
|
227 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; |
|
228 |
|
229 if (ResData == NULL) return FALSE; // Housekeeping |
|
230 |
|
231 if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing |
|
232 |
|
233 memmove(ResData ->Block + ResData ->Pointer, Ptr, size); |
|
234 ResData ->Pointer += size; |
|
235 |
|
236 if (ResData ->Pointer > iohandler->UsedSpace) |
|
237 iohandler->UsedSpace = ResData ->Pointer; |
|
238 |
|
239 |
|
240 iohandler->UsedSpace += size; |
|
241 |
|
242 return TRUE; |
|
243 } |
|
244 |
|
245 |
|
246 static |
|
247 cmsBool MemoryClose(struct _cms_io_handler* iohandler) |
|
248 { |
|
249 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; |
|
250 |
|
251 if (ResData ->FreeBlockOnClose) { |
|
252 |
|
253 if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block); |
|
254 } |
|
255 |
|
256 _cmsFree(iohandler ->ContextID, ResData); |
|
257 _cmsFree(iohandler ->ContextID, iohandler); |
|
258 |
|
259 return TRUE; |
|
260 } |
|
261 |
|
262 // Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes |
|
263 // a copy of the memory block for letting user to free the memory after invoking open profile. In write |
|
264 // mode ("w"), Buffere points to the begin of memory block to be written. |
|
265 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode) |
|
266 { |
|
267 cmsIOHANDLER* iohandler = NULL; |
|
268 FILEMEM* fm = NULL; |
|
269 |
|
270 _cmsAssert(AccessMode != NULL); |
|
271 |
|
272 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); |
|
273 if (iohandler == NULL) return NULL; |
|
274 |
|
275 switch (*AccessMode) { |
|
276 |
|
277 case 'r': |
|
278 fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); |
|
279 if (fm == NULL) goto Error; |
|
280 |
|
281 if (Buffer == NULL) { |
|
282 cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer"); |
|
283 goto Error; |
|
284 } |
|
285 |
|
286 fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size); |
81 if (fm ->Block == NULL) { |
287 if (fm ->Block == NULL) { |
82 _cmsFree(fm); |
288 |
|
289 _cmsFree(ContextID, fm); |
|
290 _cmsFree(ContextID, iohandler); |
|
291 cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", size); |
83 return NULL; |
292 return NULL; |
84 } |
293 } |
85 |
294 |
86 CopyMemory(fm->Block, Block, Size); |
295 |
|
296 memmove(fm->Block, Buffer, size); |
87 fm ->FreeBlockOnClose = TRUE; |
297 fm ->FreeBlockOnClose = TRUE; |
88 } |
298 fm ->Size = size; |
89 else { |
299 fm ->Pointer = 0; |
90 fm ->Block = Block; |
300 break; |
|
301 |
|
302 case 'w': |
|
303 fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); |
|
304 if (fm == NULL) goto Error; |
|
305 |
|
306 fm ->Block = (cmsUInt8Number*) Buffer; |
91 fm ->FreeBlockOnClose = FALSE; |
307 fm ->FreeBlockOnClose = FALSE; |
92 } |
308 fm ->Size = size; |
93 |
309 fm ->Pointer = 0; |
94 fm ->Size = Size; |
310 break; |
95 fm ->Pointer = 0; |
311 |
96 |
312 default: |
97 return (LPVOID) fm; |
313 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknow access mode '%c'", *AccessMode); |
98 } |
314 return NULL; |
99 |
315 } |
100 |
316 |
101 static |
317 iohandler ->ContextID = ContextID; |
102 size_t MemoryRead(LPVOID buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc) |
318 iohandler ->stream = (void*) fm; |
103 { |
319 iohandler ->UsedSpace = 0; |
104 FILEMEM* ResData = (FILEMEM*) Icc ->stream; |
320 iohandler ->PhysicalFile[0] = 0; |
105 LPBYTE Ptr; |
321 |
106 size_t len = size * count; |
322 iohandler ->Read = MemoryRead; |
107 size_t extent = ResData -> Pointer + len; |
323 iohandler ->Seek = MemorySeek; |
108 |
324 iohandler ->Close = MemoryClose; |
109 if (len == 0) { |
325 iohandler ->Tell = MemoryTell; |
110 return 0; |
326 iohandler ->Write = MemoryWrite; |
|
327 |
|
328 return iohandler; |
|
329 |
|
330 Error: |
|
331 if (fm) _cmsFree(ContextID, fm); |
|
332 if (iohandler) _cmsFree(ContextID, iohandler); |
|
333 return NULL; |
|
334 } |
|
335 |
|
336 // File-based stream ------------------------------------------------------- |
|
337 |
|
338 // Read count elements of size bytes each. Return number of elements read |
|
339 static |
|
340 cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) |
|
341 { |
|
342 cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream); |
|
343 |
|
344 if (nReaded != count) { |
|
345 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); |
|
346 return 0; |
|
347 } |
|
348 |
|
349 return nReaded; |
|
350 } |
|
351 |
|
352 // Postion file pointer in the file |
|
353 static |
|
354 cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) |
|
355 { |
|
356 if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) { |
|
357 |
|
358 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file"); |
|
359 return FALSE; |
|
360 } |
|
361 |
|
362 return TRUE; |
|
363 } |
|
364 |
|
365 // Returns file pointer position |
|
366 static |
|
367 cmsUInt32Number FileTell(cmsIOHANDLER* iohandler) |
|
368 { |
|
369 return ftell((FILE*)iohandler ->stream); |
|
370 } |
|
371 |
|
372 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error |
|
373 static |
|
374 cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer) |
|
375 { |
|
376 if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written |
|
377 |
|
378 iohandler->UsedSpace += size; |
|
379 return (fwrite(Buffer, size, 1, (FILE*) iohandler->stream) == 1); |
|
380 } |
|
381 |
|
382 // Closes the file |
|
383 static |
|
384 cmsBool FileClose(cmsIOHANDLER* iohandler) |
|
385 { |
|
386 if (fclose((FILE*) iohandler ->stream) != 0) return FALSE; |
|
387 _cmsFree(iohandler ->ContextID, iohandler); |
|
388 return TRUE; |
|
389 } |
|
390 |
|
391 // Create a iohandler for disk based files. if FileName is NULL, then 'stream' member is also set |
|
392 // to NULL and no real writting is performed. This only happens in writting access mode |
|
393 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode) |
|
394 { |
|
395 cmsIOHANDLER* iohandler = NULL; |
|
396 FILE* fm = NULL; |
|
397 |
|
398 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); |
|
399 if (iohandler == NULL) return NULL; |
|
400 |
|
401 switch (*AccessMode) { |
|
402 |
|
403 case 'r': |
|
404 fm = fopen(FileName, "rb"); |
|
405 if (fm == NULL) { |
|
406 _cmsFree(ContextID, iohandler); |
|
407 cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); |
|
408 return NULL; |
111 } |
409 } |
112 |
410 break; |
113 if (len / size != count) { |
411 |
114 cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Integer overflow with count / size."); |
412 case 'w': |
115 return 0; |
413 fm = fopen(FileName, "wb"); |
116 } |
414 if (fm == NULL) { |
117 |
415 _cmsFree(ContextID, iohandler); |
118 if (extent < len || extent < ResData -> Pointer) { |
416 cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); |
119 cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Integer overflow with len."); |
417 return NULL; |
120 return 0; |
418 } |
121 } |
419 break; |
122 |
420 |
123 if (ResData -> Pointer + len > ResData -> Size) { |
421 default: |
124 |
422 _cmsFree(ContextID, iohandler); |
125 len = (ResData -> Size - ResData -> Pointer); |
423 cmsSignalError(ContextID, cmsERROR_FILE, "Unknow access mode '%c'", *AccessMode); |
126 cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Got %d bytes, block should be of %d bytes", len * size, count * size); |
424 return NULL; |
127 return 0; |
425 } |
128 } |
426 |
129 |
427 iohandler ->ContextID = ContextID; |
130 Ptr = ResData -> Block; |
428 iohandler ->stream = (void*) fm; |
131 Ptr += ResData -> Pointer; |
429 iohandler ->UsedSpace = 0; |
132 CopyMemory(buffer, Ptr, len); |
430 |
133 ResData -> Pointer += (int) len; |
431 // Keep track of the original file |
134 |
432 if (FileName != NULL) { |
135 return count; |
433 |
136 } |
434 strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1); |
137 |
435 iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0; |
138 // SEEK_CUR is assumed |
436 } |
139 |
437 |
140 static |
438 iohandler ->Read = FileRead; |
141 LCMSBOOL MemorySeek(struct _lcms_iccprofile_struct* Icc, size_t offset) |
439 iohandler ->Seek = FileSeek; |
142 { |
440 iohandler ->Close = FileClose; |
143 FILEMEM* ResData = (FILEMEM*) Icc ->stream; |
441 iohandler ->Tell = FileTell; |
144 |
442 iohandler ->Write = FileWrite; |
145 if (offset > ResData ->Size) { |
443 |
146 cmsSignalError(LCMS_ERRC_ABORTED, "Pointer error; probably corrupted file"); |
444 return iohandler; |
147 return TRUE; |
445 } |
148 } |
446 |
149 |
447 // Create a iohandler for stream based files |
150 ResData ->Pointer = (DWORD) offset; |
448 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream) |
151 return FALSE; |
449 { |
152 } |
450 cmsIOHANDLER* iohandler = NULL; |
153 |
451 |
154 // FTell |
452 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); |
155 |
453 if (iohandler == NULL) return NULL; |
156 static |
454 |
157 size_t MemoryTell(struct _lcms_iccprofile_struct* Icc) |
455 iohandler -> ContextID = ContextID; |
158 { |
456 iohandler -> stream = (void*) Stream; |
159 FILEMEM* ResData = (FILEMEM*) Icc ->stream; |
457 iohandler -> UsedSpace = 0; |
160 |
458 iohandler -> PhysicalFile[0] = 0; |
161 return ResData -> Pointer; |
459 |
162 } |
460 iohandler ->Read = FileRead; |
163 |
461 iohandler ->Seek = FileSeek; |
164 |
462 iohandler ->Close = FileClose; |
165 // Writes data to memory, also keeps used space for further reference. NO CHECK IS PERFORMED |
463 iohandler ->Tell = FileTell; |
166 |
464 iohandler ->Write = FileWrite; |
167 static |
465 |
168 LCMSBOOL MemoryWrite(struct _lcms_iccprofile_struct* Icc, size_t size, void *Ptr) |
466 return iohandler; |
169 { |
467 } |
170 FILEMEM* ResData = (FILEMEM*) Icc ->stream; |
468 |
171 |
469 |
172 if (size == 0) return TRUE; |
470 |
173 |
471 // Close an open IO handler |
174 if (ResData != NULL) |
472 cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) |
175 CopyMemory(ResData ->Block + ResData ->Pointer, Ptr, size); |
473 { |
176 |
474 return io -> Close(io); |
177 ResData->Pointer += size; |
475 } |
178 Icc->UsedSpace += size; |
476 |
179 |
477 // ------------------------------------------------------------------------------------------------------- |
180 return TRUE; |
|
181 } |
|
182 |
|
183 |
|
184 static |
|
185 LCMSBOOL MemoryGrow(struct _lcms_iccprofile_struct* Icc, size_t size) |
|
186 { |
|
187 FILEMEM* ResData = (FILEMEM*) Icc->stream; |
|
188 |
|
189 void* newBlock = NULL; |
|
190 |
|
191 /* Follow same policies as functions in lcms.h */ |
|
192 if (ResData->Size + size < 0) return NULL; |
|
193 if (ResData->Size + size > ((size_t)1024*1024*500)) return NULL; |
|
194 |
|
195 newBlock = realloc(ResData->Block, ResData->Size + size); |
|
196 |
|
197 if (!newBlock) { |
|
198 return FALSE; |
|
199 } |
|
200 ResData->Block = newBlock; |
|
201 ResData->Size += size; |
|
202 return TRUE; |
|
203 } |
|
204 |
|
205 |
|
206 static |
|
207 LCMSBOOL MemoryClose(struct _lcms_iccprofile_struct* Icc) |
|
208 { |
|
209 FILEMEM* ResData = (FILEMEM*) Icc ->stream; |
|
210 |
|
211 if (ResData ->FreeBlockOnClose) { |
|
212 |
|
213 if (ResData ->Block) _cmsFree(ResData ->Block); |
|
214 } |
|
215 _cmsFree(ResData); |
|
216 return 0; |
|
217 } |
|
218 |
|
219 |
|
220 // File-based stream ------------------------------------------------------- |
|
221 |
|
222 static |
|
223 LPVOID FileOpen(const char* filename) |
|
224 { |
|
225 return (void*) fopen(filename, "rb"); |
|
226 } |
|
227 |
|
228 static |
|
229 size_t FileRead(void *buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc) |
|
230 { |
|
231 size_t nReaded = fread(buffer, size, count, (FILE*) Icc->stream); |
|
232 if (nReaded != count) { |
|
233 cmsSignalError(LCMS_ERRC_ABORTED, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); |
|
234 return 0; |
|
235 } |
|
236 |
|
237 return nReaded; |
|
238 } |
|
239 |
|
240 |
|
241 static |
|
242 LCMSBOOL FileSeek(struct _lcms_iccprofile_struct* Icc, size_t offset) |
|
243 { |
|
244 if (fseek((FILE*) Icc ->stream, (long) offset, SEEK_SET) != 0) { |
|
245 |
|
246 cmsSignalError(LCMS_ERRC_ABORTED, "Seek error; probably corrupted file"); |
|
247 return TRUE; |
|
248 } |
|
249 |
|
250 return FALSE; |
|
251 } |
|
252 |
|
253 |
|
254 static |
|
255 size_t FileTell(struct _lcms_iccprofile_struct* Icc) |
|
256 { |
|
257 return ftell((FILE*) Icc ->stream); |
|
258 } |
|
259 |
|
260 // Writes data to stream, also keeps used space for further reference |
|
261 |
|
262 |
|
263 static |
|
264 LCMSBOOL FileWrite(struct _lcms_iccprofile_struct* Icc, size_t size, LPVOID Ptr) |
|
265 { |
|
266 if (size == 0) return TRUE; |
|
267 |
|
268 Icc->UsedSpace += size; |
|
269 |
|
270 if (Icc->stream == NULL) { |
|
271 |
|
272 return TRUE; |
|
273 } |
|
274 |
|
275 return (fwrite(Ptr, size, 1, (FILE*) Icc->stream) == 1); |
|
276 } |
|
277 |
|
278 |
|
279 static |
|
280 LCMSBOOL FileGrow(struct _lcms_iccprofile_struct* Icc, size_t size) |
|
281 { |
|
282 return TRUE; |
|
283 } |
|
284 |
|
285 |
|
286 static |
|
287 LCMSBOOL FileClose(struct _lcms_iccprofile_struct* Icc) |
|
288 { |
|
289 return fclose((FILE*) Icc ->stream); |
|
290 } |
|
291 |
|
292 // ---------------------------------------------------------------------------------------------------- |
|
293 |
|
294 |
478 |
295 // Creates an empty structure holding all required parameters |
479 // Creates an empty structure holding all required parameters |
296 |
480 cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) |
297 cmsHPROFILE _cmsCreateProfilePlaceholder(void) |
481 { |
298 { |
482 time_t now = time(NULL); |
299 |
483 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); |
300 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) _cmsMalloc(sizeof(LCMSICCPROFILE)); |
|
301 if (Icc == NULL) return NULL; |
484 if (Icc == NULL) return NULL; |
302 |
485 |
303 // Empty values |
486 Icc ->ContextID = ContextID; |
304 ZeroMemory(Icc, sizeof(LCMSICCPROFILE)); |
|
305 |
|
306 // Make sure illuminant is correct |
|
307 Icc ->Illuminant = *cmsD50_XYZ(); |
|
308 |
487 |
309 // Set it to empty |
488 // Set it to empty |
310 Icc -> TagCount = 0; |
489 Icc -> TagCount = 0; |
311 |
490 |
|
491 // Set default version |
|
492 Icc ->Version = 0x02100000; |
|
493 |
|
494 // Set creation date/time |
|
495 memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created)); |
|
496 |
312 // Return the handle |
497 // Return the handle |
313 return (cmsHPROFILE) Icc; |
498 return (cmsHPROFILE) Icc; |
314 } |
499 } |
315 |
500 |
|
501 cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) |
|
502 { |
|
503 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
504 |
|
505 if (Icc == NULL) return NULL; |
|
506 return Icc -> ContextID; |
|
507 } |
|
508 |
316 |
509 |
317 // Return the number of tags |
510 // Return the number of tags |
318 icInt32Number LCMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) |
511 cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) |
319 { |
512 { |
320 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
513 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
514 if (Icc == NULL) return -1; |
|
515 |
321 return Icc->TagCount; |
516 return Icc->TagCount; |
322 } |
517 } |
323 |
518 |
324 // Return the tag signature of a given tag number |
519 // Return the tag signature of a given tag number |
325 icTagSignature LCMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, icInt32Number n) |
520 cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n) |
326 { |
521 { |
327 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
522 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
328 |
523 |
329 if (n < 0 || n > Icc->TagCount) return (icTagSignature) 0; // Mark as not available |
524 if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available |
|
525 if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check |
330 |
526 |
331 return Icc ->TagNames[n]; |
527 return Icc ->TagNames[n]; |
332 } |
528 } |
333 |
529 |
334 |
530 |
335 // Search for a specific tag in tag dictionary |
531 static |
336 // Returns position or -1 if tag not found |
532 int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig) |
337 |
533 { |
338 icInt32Number _cmsSearchTag(LPLCMSICCPROFILE Profile, icTagSignature sig, LCMSBOOL lSignalError) |
534 cmsUInt32Number i; |
339 { |
535 |
340 icInt32Number i; |
536 for (i=0; i < Profile -> TagCount; i++) { |
341 |
537 |
342 if (sig == 0) return -1; // 0 identifies a special tag holding raw memory. |
538 if (sig == Profile -> TagNames[i]) |
343 |
539 return i; |
344 for (i=0; i < Profile -> TagCount; i++) { |
540 } |
345 |
541 |
346 if (sig == Profile -> TagNames[i]) |
542 return -1; |
347 return i; |
543 } |
348 } |
544 |
349 |
545 // Search for a specific tag in tag dictionary. Returns position or -1 if tag not found. |
350 if (lSignalError) |
546 // If followlinks is turned on, then the position of the linked tag is returned |
351 cmsSignalError(LCMS_ERRC_ABORTED, "Tag '%lx' not found", sig); |
547 int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks) |
352 |
548 { |
353 return -1; |
549 int n; |
|
550 cmsTagSignature LinkedSig; |
|
551 |
|
552 do { |
|
553 |
|
554 // Search for given tag in ICC profile directory |
|
555 n = SearchOneTag(Icc, sig); |
|
556 if (n < 0) |
|
557 return -1; // Not found |
|
558 |
|
559 if (!lFollowLinks) |
|
560 return n; // Found, don't follow links |
|
561 |
|
562 // Is this a linked tag? |
|
563 LinkedSig = Icc ->TagLinked[n]; |
|
564 |
|
565 // Yes, follow link |
|
566 if (LinkedSig != (cmsTagSignature) 0) { |
|
567 sig = LinkedSig; |
|
568 } |
|
569 |
|
570 } while (LinkedSig != (cmsTagSignature) 0); |
|
571 |
|
572 return n; |
|
573 } |
|
574 |
|
575 |
|
576 // Create a new tag entry |
|
577 |
|
578 static |
|
579 cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) |
|
580 { |
|
581 int i; |
|
582 |
|
583 // Search for the tag |
|
584 i = _cmsSearchTag(Icc, sig, FALSE); |
|
585 |
|
586 // Now let's do it easy. If the tag has been already written, that's an error |
|
587 if (i >= 0) { |
|
588 cmsSignalError(Icc ->ContextID, cmsERROR_ALREADY_DEFINED, "Tag '%x' already exists", sig); |
|
589 return FALSE; |
|
590 } |
|
591 else { |
|
592 |
|
593 // New one |
|
594 |
|
595 if (Icc -> TagCount >= MAX_TABLE_TAG) { |
|
596 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); |
|
597 return FALSE; |
|
598 } |
|
599 |
|
600 *NewPos = Icc ->TagCount; |
|
601 Icc -> TagCount++; |
|
602 } |
|
603 |
|
604 return TRUE; |
354 } |
605 } |
355 |
606 |
356 |
607 |
357 // Check existance |
608 // Check existance |
358 |
609 cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) |
359 LCMSBOOL LCMSEXPORT cmsIsTag(cmsHPROFILE hProfile, icTagSignature sig) |
610 { |
360 { |
611 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile; |
361 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
362 return _cmsSearchTag(Icc, sig, FALSE) >= 0; |
612 return _cmsSearchTag(Icc, sig, FALSE) >= 0; |
363 } |
613 } |
364 |
614 |
365 |
615 |
366 |
616 // Read profile header and validate it |
367 // Search for a particular tag, replace if found or add new one else |
617 cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) |
368 |
618 { |
369 LPVOID _cmsInitTag(LPLCMSICCPROFILE Icc, icTagSignature sig, size_t size, const void* Init) |
619 cmsTagEntry Tag; |
370 { |
620 cmsICCHeader Header; |
371 LPVOID Ptr; |
621 cmsUInt32Number i, j; |
372 icInt32Number i; |
622 cmsUInt32Number HeaderSize; |
|
623 cmsIOHANDLER* io = Icc ->IOhandler; |
|
624 cmsUInt32Number TagCount; |
|
625 |
|
626 |
|
627 // Read the header |
|
628 if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) { |
|
629 return FALSE; |
|
630 } |
|
631 |
|
632 // Validate file as an ICC profile |
|
633 if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) { |
|
634 cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature"); |
|
635 return FALSE; |
|
636 } |
|
637 |
|
638 // Adjust endianess of the used parameters |
|
639 Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass); |
|
640 Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace); |
|
641 Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs); |
|
642 Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent); |
|
643 Icc -> flags = _cmsAdjustEndianess32(Header.flags); |
|
644 Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer); |
|
645 Icc -> model = _cmsAdjustEndianess32(Header.model); |
|
646 _cmsAdjustEndianess64(&Icc -> attributes, Header.attributes); |
|
647 Icc -> Version = _cmsAdjustEndianess32(Header.version); |
|
648 |
|
649 // Get size as reported in header |
|
650 HeaderSize = _cmsAdjustEndianess32(Header.size); |
|
651 |
|
652 // Get creation date/time |
|
653 _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created); |
|
654 |
|
655 // The profile ID are 32 raw bytes |
|
656 memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16); |
|
657 |
|
658 |
|
659 // Read tag directory |
|
660 if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE; |
|
661 if (TagCount > MAX_TABLE_TAG) { |
|
662 |
|
663 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount); |
|
664 return FALSE; |
|
665 } |
|
666 |
|
667 // Read tag directory |
|
668 Icc -> TagCount = 0; |
|
669 for (i=0; i < TagCount; i++) { |
|
670 |
|
671 if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE; |
|
672 if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE; |
|
673 if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; |
|
674 |
|
675 // Perform some sanity check. Offset + size should fall inside file. |
|
676 if (Tag.offset + Tag.size > HeaderSize) |
|
677 continue; |
|
678 |
|
679 Icc -> TagNames[Icc ->TagCount] = Tag.sig; |
|
680 Icc -> TagOffsets[Icc ->TagCount] = Tag.offset; |
|
681 Icc -> TagSizes[Icc ->TagCount] = Tag.size; |
|
682 |
|
683 // Search for links |
|
684 for (j=0; j < Icc ->TagCount; j++) { |
|
685 |
|
686 if ((Icc ->TagOffsets[j] == Tag.offset) && |
|
687 (Icc ->TagSizes[j] == Tag.size)) { |
|
688 |
|
689 Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; |
|
690 } |
|
691 |
|
692 } |
|
693 |
|
694 Icc ->TagCount++; |
|
695 } |
|
696 |
|
697 return TRUE; |
|
698 } |
|
699 |
|
700 // Saves profile header |
|
701 cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) |
|
702 { |
|
703 cmsICCHeader Header; |
|
704 cmsUInt32Number i; |
|
705 cmsTagEntry Tag; |
|
706 cmsInt32Number Count = 0; |
|
707 |
|
708 Header.size = _cmsAdjustEndianess32(UsedSpace); |
|
709 Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); |
|
710 Header.version = _cmsAdjustEndianess32(Icc ->Version); |
|
711 |
|
712 Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); |
|
713 Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); |
|
714 Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); |
|
715 |
|
716 // NOTE: in v4 Timestamp must be in UTC rather than in local time |
|
717 _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); |
|
718 |
|
719 Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); |
|
720 |
|
721 #ifdef CMS_IS_WINDOWS_ |
|
722 Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); |
|
723 #else |
|
724 Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); |
|
725 #endif |
|
726 |
|
727 Header.flags = _cmsAdjustEndianess32(Icc -> flags); |
|
728 Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); |
|
729 Header.model = _cmsAdjustEndianess32(Icc -> model); |
|
730 |
|
731 _cmsAdjustEndianess64(&Header.attributes, Icc -> attributes); |
|
732 |
|
733 // Rendering intent in the header (for embedded profiles) |
|
734 Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); |
|
735 |
|
736 // Illuminant is always D50 |
|
737 Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); |
|
738 Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); |
|
739 Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); |
|
740 |
|
741 // Created by LittleCMS (that's me!) |
|
742 Header.creator = _cmsAdjustEndianess32(lcmsSignature); |
|
743 |
|
744 memset(&Header.reserved, 0, sizeof(Header.reserved)); |
|
745 |
|
746 // Set profile ID. Endianess is always big endian |
|
747 memmove(&Header.profileID, &Icc ->ProfileID, 16); |
|
748 |
|
749 // Dump the header |
|
750 if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; |
|
751 |
|
752 // Saves Tag directory |
|
753 |
|
754 // Get true count |
|
755 for (i=0; i < Icc -> TagCount; i++) { |
|
756 if (Icc ->TagNames[i] != 0) |
|
757 Count++; |
|
758 } |
|
759 |
|
760 // Store number of tags |
|
761 if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; |
|
762 |
|
763 for (i=0; i < Icc -> TagCount; i++) { |
|
764 |
|
765 if (Icc ->TagNames[i] == 0) continue; // It is just a placeholder |
|
766 |
|
767 Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]); |
|
768 Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]); |
|
769 Tag.size = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]); |
|
770 |
|
771 if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; |
|
772 } |
|
773 |
|
774 return TRUE; |
|
775 } |
|
776 |
|
777 // ----------------------------------------------------------------------- Set/Get several struct members |
|
778 |
|
779 |
|
780 cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) |
|
781 { |
|
782 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
783 return Icc -> RenderingIntent; |
|
784 } |
|
785 |
|
786 void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) |
|
787 { |
|
788 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
789 Icc -> RenderingIntent = RenderingIntent; |
|
790 } |
|
791 |
|
792 cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) |
|
793 { |
|
794 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
795 return (cmsUInt32Number) Icc -> flags; |
|
796 } |
|
797 |
|
798 void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) |
|
799 { |
|
800 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
801 Icc -> flags = (cmsUInt32Number) Flags; |
|
802 } |
|
803 |
|
804 cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) |
|
805 { |
|
806 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
807 return (cmsUInt32Number) Icc ->manufacturer; |
|
808 } |
|
809 |
|
810 void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) |
|
811 { |
|
812 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
813 Icc -> manufacturer = (cmsUInt32Number) manufacturer; |
|
814 } |
|
815 |
|
816 cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) |
|
817 { |
|
818 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
819 return (cmsUInt32Number) Icc ->model; |
|
820 } |
|
821 |
|
822 void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) |
|
823 { |
|
824 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
825 Icc -> manufacturer = (cmsUInt32Number) model; |
|
826 } |
|
827 |
|
828 |
|
829 void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) |
|
830 { |
|
831 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
832 memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); |
|
833 } |
|
834 |
|
835 void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) |
|
836 { |
|
837 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
838 memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); |
|
839 } |
|
840 |
|
841 void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) |
|
842 { |
|
843 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
844 memmove(ProfileID, Icc ->ProfileID.ID8, 16); |
|
845 } |
|
846 |
|
847 void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) |
|
848 { |
|
849 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
850 memmove(&Icc -> ProfileID, ProfileID, 16); |
|
851 } |
|
852 |
|
853 cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) |
|
854 { |
|
855 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
856 memmove(Dest, &Icc ->Created, sizeof(struct tm)); |
|
857 return TRUE; |
|
858 } |
|
859 |
|
860 cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) |
|
861 { |
|
862 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
863 return Icc -> PCS; |
|
864 } |
|
865 |
|
866 void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) |
|
867 { |
|
868 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
869 Icc -> PCS = pcs; |
|
870 } |
|
871 |
|
872 cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) |
|
873 { |
|
874 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
875 return Icc -> ColorSpace; |
|
876 } |
|
877 |
|
878 void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) |
|
879 { |
|
880 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
881 Icc -> ColorSpace = sig; |
|
882 } |
|
883 |
|
884 cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) |
|
885 { |
|
886 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
887 return Icc -> DeviceClass; |
|
888 } |
|
889 |
|
890 void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) |
|
891 { |
|
892 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
893 Icc -> DeviceClass = sig; |
|
894 } |
|
895 |
|
896 cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) |
|
897 { |
|
898 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
899 return Icc -> Version; |
|
900 } |
|
901 |
|
902 void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) |
|
903 { |
|
904 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
905 Icc -> Version = Version; |
|
906 } |
|
907 |
|
908 // Get an hexadecimal number with same digits as v |
|
909 static |
|
910 cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) |
|
911 { |
|
912 char Buff[100]; |
|
913 int i, len; |
|
914 cmsUInt32Number out; |
|
915 |
|
916 for (len=0; in > 0 && len < 100; len++) { |
|
917 |
|
918 Buff[len] = (char) (in % BaseIn); |
|
919 in /= BaseIn; |
|
920 } |
|
921 |
|
922 for (i=len-1, out=0; i >= 0; --i) { |
|
923 out = out * BaseOut + Buff[i]; |
|
924 } |
|
925 |
|
926 return out; |
|
927 } |
|
928 |
|
929 void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) |
|
930 { |
|
931 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
932 |
|
933 // 4.2 -> 0x4200000 |
|
934 |
|
935 Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0), 10, 16) << 16; |
|
936 } |
|
937 |
|
938 cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) |
|
939 { |
|
940 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
941 cmsUInt32Number n = Icc -> Version >> 16; |
|
942 |
|
943 return BaseToBase(n, 16, 10) / 100.0; |
|
944 } |
|
945 // -------------------------------------------------------------------------------------------------------------- |
|
946 |
|
947 |
|
948 // Create profile from IOhandler |
|
949 cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) |
|
950 { |
|
951 _cmsICCPROFILE* NewIcc; |
|
952 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); |
|
953 |
|
954 if (hEmpty == NULL) return NULL; |
|
955 |
|
956 NewIcc = (_cmsICCPROFILE*) hEmpty; |
|
957 |
|
958 NewIcc ->IOhandler = io; |
|
959 if (!_cmsReadHeader(NewIcc)) goto Error; |
|
960 return hEmpty; |
|
961 |
|
962 Error: |
|
963 cmsCloseProfile(hEmpty); |
|
964 return NULL; |
|
965 } |
|
966 |
|
967 // Create profile from disk file |
|
968 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) |
|
969 { |
|
970 _cmsICCPROFILE* NewIcc; |
|
971 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); |
|
972 |
|
973 if (hEmpty == NULL) return NULL; |
|
974 |
|
975 NewIcc = (_cmsICCPROFILE*) hEmpty; |
|
976 |
|
977 NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); |
|
978 if (NewIcc ->IOhandler == NULL) goto Error; |
|
979 |
|
980 if (*sAccess == 'W' || *sAccess == 'w') { |
|
981 |
|
982 NewIcc -> IsWrite = TRUE; |
|
983 |
|
984 return hEmpty; |
|
985 } |
|
986 |
|
987 if (!_cmsReadHeader(NewIcc)) goto Error; |
|
988 return hEmpty; |
|
989 |
|
990 Error: |
|
991 cmsCloseProfile(hEmpty); |
|
992 return NULL; |
|
993 } |
|
994 |
|
995 |
|
996 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) |
|
997 { |
|
998 return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); |
|
999 } |
|
1000 |
|
1001 |
|
1002 cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) |
|
1003 { |
|
1004 _cmsICCPROFILE* NewIcc; |
|
1005 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); |
|
1006 |
|
1007 if (hEmpty == NULL) return NULL; |
|
1008 |
|
1009 NewIcc = (_cmsICCPROFILE*) hEmpty; |
|
1010 |
|
1011 NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); |
|
1012 if (NewIcc ->IOhandler == NULL) goto Error; |
|
1013 |
|
1014 if (*sAccess == 'w') { |
|
1015 |
|
1016 NewIcc -> IsWrite = TRUE; |
|
1017 return hEmpty; |
|
1018 } |
|
1019 |
|
1020 if (!_cmsReadHeader(NewIcc)) goto Error; |
|
1021 return hEmpty; |
|
1022 |
|
1023 Error: |
|
1024 cmsCloseProfile(hEmpty); |
|
1025 return NULL; |
|
1026 |
|
1027 } |
|
1028 |
|
1029 cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) |
|
1030 { |
|
1031 return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); |
|
1032 } |
|
1033 |
|
1034 |
|
1035 // Open from memory block |
|
1036 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) |
|
1037 { |
|
1038 _cmsICCPROFILE* NewIcc; |
|
1039 cmsHPROFILE hEmpty; |
|
1040 |
|
1041 hEmpty = cmsCreateProfilePlaceholder(ContextID); |
|
1042 if (hEmpty == NULL) return NULL; |
|
1043 |
|
1044 NewIcc = (_cmsICCPROFILE*) hEmpty; |
|
1045 |
|
1046 // Ok, in this case const void* is casted to void* just because open IO handler |
|
1047 // shares read and writting modes. Don't abuse this feature! |
|
1048 NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); |
|
1049 if (NewIcc ->IOhandler == NULL) goto Error; |
|
1050 |
|
1051 if (!_cmsReadHeader(NewIcc)) goto Error; |
|
1052 |
|
1053 return hEmpty; |
|
1054 |
|
1055 Error: |
|
1056 cmsCloseProfile(hEmpty); |
|
1057 return NULL; |
|
1058 } |
|
1059 |
|
1060 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) |
|
1061 { |
|
1062 return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); |
|
1063 } |
|
1064 |
|
1065 |
|
1066 |
|
1067 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig |
|
1068 static |
|
1069 cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) |
|
1070 { |
|
1071 cmsUInt8Number* Data; |
|
1072 cmsUInt32Number i; |
|
1073 cmsUInt32Number Begin; |
|
1074 cmsIOHANDLER* io = Icc ->IOhandler; |
|
1075 cmsTagDescriptor* TagDescriptor; |
|
1076 cmsTagTypeSignature TypeBase; |
|
1077 cmsTagTypeHandler* TypeHandler; |
|
1078 |
|
1079 |
|
1080 for (i=0; i < Icc -> TagCount; i++) { |
|
1081 |
|
1082 |
|
1083 if (Icc ->TagNames[i] == 0) continue; |
|
1084 |
|
1085 // Linked tags are not written |
|
1086 if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; |
|
1087 |
|
1088 Icc -> TagOffsets[i] = Begin = io ->UsedSpace; |
|
1089 |
|
1090 Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; |
|
1091 |
|
1092 if (!Data) { |
|
1093 |
|
1094 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. |
|
1095 // In this case a blind copy of the block data is performed |
|
1096 if (FileOrig != NULL && Icc -> TagOffsets[i]) { |
|
1097 |
|
1098 cmsUInt32Number TagSize = FileOrig -> TagSizes[i]; |
|
1099 cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i]; |
|
1100 void* Mem; |
|
1101 |
|
1102 if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE; |
|
1103 |
|
1104 Mem = _cmsMalloc(Icc ->ContextID, TagSize); |
|
1105 if (Mem == NULL) return FALSE; |
|
1106 |
|
1107 if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; |
|
1108 if (!io ->Write(io, TagSize, Mem)) return FALSE; |
|
1109 _cmsFree(Icc ->ContextID, Mem); |
|
1110 |
|
1111 Icc -> TagSizes[i] = (io ->UsedSpace - Begin); |
|
1112 |
|
1113 |
|
1114 // Align to 32 bit boundary. |
|
1115 if (! _cmsWriteAlignment(io)) |
|
1116 return FALSE; |
|
1117 } |
|
1118 |
|
1119 continue; |
|
1120 } |
|
1121 |
|
1122 |
|
1123 // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) |
|
1124 if (Icc ->TagSaveAsRaw[i]) { |
|
1125 |
|
1126 if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; |
|
1127 } |
|
1128 else { |
|
1129 |
|
1130 // Search for support on this tag |
|
1131 TagDescriptor = _cmsGetTagDescriptor(Icc -> TagNames[i]); |
|
1132 if (TagDescriptor == NULL) continue; // Unsupported, ignore it |
|
1133 |
|
1134 TypeHandler = Icc ->TagTypeHandlers[i]; |
|
1135 |
|
1136 if (TypeHandler == NULL) { |
|
1137 cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); |
|
1138 continue; |
|
1139 } |
|
1140 |
|
1141 TypeBase = TypeHandler ->Signature; |
|
1142 if (!_cmsWriteTypeBase(io, TypeBase)) |
|
1143 return FALSE; |
|
1144 |
|
1145 if (!TypeHandler ->WritePtr(TypeHandler, io, Data, TagDescriptor ->ElemCount)) { |
|
1146 |
|
1147 char String[5]; |
|
1148 |
|
1149 _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); |
|
1150 cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); |
|
1151 return FALSE; |
|
1152 } |
|
1153 } |
|
1154 |
|
1155 |
|
1156 Icc -> TagSizes[i] = (io ->UsedSpace - Begin); |
|
1157 |
|
1158 // Align to 32 bit boundary. |
|
1159 if (! _cmsWriteAlignment(io)) |
|
1160 return FALSE; |
|
1161 } |
|
1162 |
|
1163 |
|
1164 return TRUE; |
|
1165 } |
|
1166 |
|
1167 |
|
1168 // Fill the offset and size fields for all linked tags |
|
1169 static |
|
1170 cmsBool SetLinks( _cmsICCPROFILE* Icc) |
|
1171 { |
|
1172 cmsUInt32Number i; |
|
1173 |
|
1174 for (i=0; i < Icc -> TagCount; i++) { |
|
1175 |
|
1176 cmsTagSignature lnk = Icc ->TagLinked[i]; |
|
1177 if (lnk != (cmsTagSignature) 0) { |
|
1178 |
|
1179 int j = _cmsSearchTag(Icc, lnk, FALSE); |
|
1180 if (j >= 0) { |
|
1181 |
|
1182 Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; |
|
1183 Icc ->TagSizes[i] = Icc ->TagSizes[j]; |
|
1184 } |
|
1185 |
|
1186 } |
|
1187 } |
|
1188 |
|
1189 return TRUE; |
|
1190 } |
|
1191 |
|
1192 // Low-level save to IOHANDLER. It returns the number of bytes used to |
|
1193 // store the profile, or zero on error. io may be NULL and in this case |
|
1194 // no data is written--only sizes are calculated |
|
1195 cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) |
|
1196 { |
|
1197 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
1198 _cmsICCPROFILE Keep; |
|
1199 cmsIOHANDLER* PrevIO; |
|
1200 cmsUInt32Number UsedSpace; |
|
1201 cmsContext ContextID; |
|
1202 |
|
1203 memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); |
|
1204 |
|
1205 ContextID = cmsGetProfileContextID(hProfile); |
|
1206 PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); |
|
1207 if (PrevIO == NULL) return 0; |
|
1208 |
|
1209 // Pass #1 does compute offsets |
|
1210 |
|
1211 if (!_cmsWriteHeader(Icc, 0)) return 0; |
|
1212 if (!SaveTags(Icc, &Keep)) return 0; |
|
1213 |
|
1214 UsedSpace = PrevIO ->UsedSpace; |
|
1215 |
|
1216 // Pass #2 does save to iohandler |
|
1217 |
|
1218 if (io != NULL) { |
|
1219 Icc ->IOhandler = io; |
|
1220 if (!SetLinks(Icc)) goto CleanUp; |
|
1221 if (!_cmsWriteHeader(Icc, UsedSpace)) goto CleanUp; |
|
1222 if (!SaveTags(Icc, &Keep)) goto CleanUp; |
|
1223 } |
|
1224 |
|
1225 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); |
|
1226 if (!cmsCloseIOhandler(PrevIO)) return 0; |
|
1227 |
|
1228 return UsedSpace; |
|
1229 |
|
1230 |
|
1231 CleanUp: |
|
1232 cmsCloseIOhandler(PrevIO); |
|
1233 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); |
|
1234 return 0; |
|
1235 } |
|
1236 |
|
1237 |
|
1238 // Low-level save to disk. |
|
1239 cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) |
|
1240 { |
|
1241 cmsContext ContextID = cmsGetProfileContextID(hProfile); |
|
1242 cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); |
|
1243 cmsBool rc; |
|
1244 |
|
1245 if (io == NULL) return FALSE; |
|
1246 |
|
1247 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); |
|
1248 rc &= cmsCloseIOhandler(io); |
|
1249 |
|
1250 if (rc == FALSE) { // remove() is C99 per 7.19.4.1 |
|
1251 remove(FileName); // We have to IGNORE return value in this case |
|
1252 } |
|
1253 return rc; |
|
1254 } |
|
1255 |
|
1256 // Same as anterior, but for streams |
|
1257 cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) |
|
1258 { |
|
1259 cmsBool rc; |
|
1260 cmsContext ContextID = cmsGetProfileContextID(hProfile); |
|
1261 cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); |
|
1262 |
|
1263 if (io == NULL) return FALSE; |
|
1264 |
|
1265 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); |
|
1266 rc &= cmsCloseIOhandler(io); |
|
1267 |
|
1268 return rc; |
|
1269 } |
|
1270 |
|
1271 |
|
1272 // Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only |
|
1273 cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) |
|
1274 { |
|
1275 cmsBool rc; |
|
1276 cmsIOHANDLER* io; |
|
1277 cmsContext ContextID = cmsGetProfileContextID(hProfile); |
|
1278 |
|
1279 // Should we just calculate the needed space? |
|
1280 if (MemPtr == NULL) { |
|
1281 |
|
1282 *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); |
|
1283 return TRUE; |
|
1284 } |
|
1285 |
|
1286 // That is a real write operation |
|
1287 io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); |
|
1288 if (io == NULL) return FALSE; |
|
1289 |
|
1290 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); |
|
1291 rc &= cmsCloseIOhandler(io); |
|
1292 |
|
1293 return rc; |
|
1294 } |
|
1295 |
|
1296 |
|
1297 |
|
1298 // Closes a profile freeing any involved resources |
|
1299 cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) |
|
1300 { |
|
1301 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
1302 cmsBool rc = TRUE; |
|
1303 cmsUInt32Number i; |
|
1304 |
|
1305 if (!Icc) return FALSE; |
|
1306 |
|
1307 // Was open in write mode? |
|
1308 if (Icc ->IsWrite) { |
|
1309 |
|
1310 Icc ->IsWrite = FALSE; // Assure no further writting |
|
1311 rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); |
|
1312 } |
|
1313 |
|
1314 for (i=0; i < Icc -> TagCount; i++) { |
|
1315 |
|
1316 if (Icc -> TagPtrs[i]) { |
|
1317 |
|
1318 cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; |
|
1319 |
|
1320 if (TypeHandler != NULL) |
|
1321 TypeHandler ->FreePtr(TypeHandler, Icc -> TagPtrs[i]); |
|
1322 else |
|
1323 _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); |
|
1324 } |
|
1325 } |
|
1326 |
|
1327 if (Icc ->IOhandler != NULL) { |
|
1328 rc &= cmsCloseIOhandler(Icc->IOhandler); |
|
1329 } |
|
1330 |
|
1331 _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory |
|
1332 |
|
1333 return rc; |
|
1334 } |
|
1335 |
|
1336 |
|
1337 // ------------------------------------------------------------------------------------------------------------------- |
|
1338 |
|
1339 |
|
1340 // Returns TRUE if a given tag is supported by a plug-in |
|
1341 static |
|
1342 cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) |
|
1343 { |
|
1344 cmsUInt32Number i, nMaxTypes; |
|
1345 |
|
1346 nMaxTypes = TagDescriptor->nSupportedTypes; |
|
1347 if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) |
|
1348 nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; |
|
1349 |
|
1350 for (i=0; i < nMaxTypes; i++) { |
|
1351 if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; |
|
1352 } |
|
1353 |
|
1354 return FALSE; |
|
1355 } |
|
1356 |
|
1357 |
|
1358 // That's the main read function |
|
1359 void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) |
|
1360 { |
|
1361 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
1362 cmsIOHANDLER* io = Icc ->IOhandler; |
|
1363 cmsTagTypeHandler* TypeHandler; |
|
1364 cmsTagDescriptor* TagDescriptor; |
|
1365 cmsTagTypeSignature BaseType; |
|
1366 cmsUInt32Number Offset, TagSize; |
|
1367 cmsUInt32Number ElemCount; |
|
1368 int n; |
|
1369 |
|
1370 n = _cmsSearchTag(Icc, sig, TRUE); |
|
1371 if (n < 0) return NULL; // Not found, return NULL |
|
1372 |
|
1373 |
|
1374 |
|
1375 // If the element is already in memory, return the pointer |
|
1376 if (Icc -> TagPtrs[n]) { |
|
1377 |
|
1378 if (Icc ->TagSaveAsRaw[n]) return NULL; // We don't support read raw tags as cooked |
|
1379 return Icc -> TagPtrs[n]; |
|
1380 } |
|
1381 |
|
1382 // We need to read it. Get the offset and size to the file |
|
1383 Offset = Icc -> TagOffsets[n]; |
|
1384 TagSize = Icc -> TagSizes[n]; |
|
1385 |
|
1386 // Seek to its location |
|
1387 if (!io -> Seek(io, Offset)) |
|
1388 return NULL; |
|
1389 |
|
1390 // Search for support on this tag |
|
1391 TagDescriptor = _cmsGetTagDescriptor(sig); |
|
1392 if (TagDescriptor == NULL) return NULL; // Unsupported. |
|
1393 |
|
1394 // if supported, get type and check if in list |
|
1395 BaseType = _cmsReadTypeBase(io); |
|
1396 if (BaseType == 0) return NULL; |
|
1397 |
|
1398 if (!IsTypeSupported(TagDescriptor, BaseType)) return NULL; |
|
1399 |
|
1400 TagSize -= 8; // Alredy read by the type base logic |
|
1401 |
|
1402 // Get type handler |
|
1403 TypeHandler = _cmsGetTagTypeHandler(BaseType); |
|
1404 if (TypeHandler == NULL) return NULL; |
|
1405 |
|
1406 |
|
1407 // Read the tag |
|
1408 Icc -> TagTypeHandlers[n] = TypeHandler; |
|
1409 Icc -> TagPtrs[n] = TypeHandler ->ReadPtr(TypeHandler, io, &ElemCount, TagSize); |
|
1410 |
|
1411 // The tag type is supported, but something wrong happend and we cannot read the tag. |
|
1412 // let know the user about this (although it is just a warning) |
|
1413 if (Icc -> TagPtrs[n] == NULL) { |
|
1414 |
|
1415 char String[5]; |
|
1416 |
|
1417 _cmsTagSignature2String(String, sig); |
|
1418 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); |
|
1419 return NULL; |
|
1420 } |
|
1421 |
|
1422 // This is a weird error that may be a symptom of something more serious, the number of |
|
1423 // stored item is actually less than the number of required elements. |
|
1424 if (ElemCount < TagDescriptor ->ElemCount) { |
|
1425 |
|
1426 char String[5]; |
|
1427 |
|
1428 _cmsTagSignature2String(String, sig); |
|
1429 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", |
|
1430 String, TagDescriptor ->ElemCount, ElemCount); |
|
1431 } |
|
1432 |
|
1433 |
|
1434 // Return the data |
|
1435 return Icc -> TagPtrs[n]; |
|
1436 } |
|
1437 |
|
1438 |
|
1439 // Get true type of data |
|
1440 cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) |
|
1441 { |
|
1442 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
1443 cmsTagTypeHandler* TypeHandler; |
|
1444 int n; |
|
1445 |
|
1446 // Search for given tag in ICC profile directory |
|
1447 n = _cmsSearchTag(Icc, sig, TRUE); |
|
1448 if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL |
|
1449 |
|
1450 // Get the handler. The true type is there |
|
1451 TypeHandler = Icc -> TagTypeHandlers[n]; |
|
1452 return TypeHandler ->Signature; |
|
1453 } |
|
1454 |
|
1455 |
|
1456 // Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already |
|
1457 // in that list, the previous version is deleted. |
|
1458 cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) |
|
1459 { |
|
1460 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
1461 cmsTagTypeHandler* TypeHandler = NULL; |
|
1462 cmsTagDescriptor* TagDescriptor = NULL; |
|
1463 cmsTagTypeSignature Type; |
|
1464 int i; |
|
1465 cmsFloat64Number Version; |
|
1466 |
|
1467 |
|
1468 if (data == NULL) { |
|
1469 |
|
1470 cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_NULL, "couldn't wite NULL to tag"); |
|
1471 return FALSE; |
|
1472 } |
373 |
1473 |
374 i = _cmsSearchTag(Icc, sig, FALSE); |
1474 i = _cmsSearchTag(Icc, sig, FALSE); |
375 |
|
376 if (i >=0) { |
1475 if (i >=0) { |
377 |
1476 |
378 if (Icc -> TagPtrs[i]) _cmsFree(Icc -> TagPtrs[i]); |
1477 if (Icc -> TagPtrs[i] != NULL) { |
|
1478 |
|
1479 // Already exists. Free previous version |
|
1480 if (Icc ->TagSaveAsRaw[i]) { |
|
1481 _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); |
|
1482 } |
|
1483 else { |
|
1484 TypeHandler = Icc ->TagTypeHandlers[i]; |
|
1485 TypeHandler->FreePtr(TypeHandler, Icc -> TagPtrs[i]); |
|
1486 } |
|
1487 } |
379 } |
1488 } |
380 else { |
1489 else { |
381 |
1490 // New one |
382 i = Icc -> TagCount; |
1491 i = Icc -> TagCount; |
|
1492 |
|
1493 if (i >= MAX_TABLE_TAG) { |
|
1494 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); |
|
1495 return FALSE; |
|
1496 } |
|
1497 |
383 Icc -> TagCount++; |
1498 Icc -> TagCount++; |
384 |
1499 } |
385 if (Icc ->TagCount >= MAX_TABLE_TAG) { |
1500 |
386 |
1501 // This is not raw |
387 cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", MAX_TABLE_TAG); |
1502 Icc ->TagSaveAsRaw[i] = FALSE; |
388 Icc ->TagCount = MAX_TABLE_TAG-1; |
1503 |
389 return NULL; |
1504 // This is not a link |
|
1505 Icc ->TagLinked[i] = (cmsTagSignature) 0; |
|
1506 |
|
1507 // Get information about the TAG. |
|
1508 TagDescriptor = _cmsGetTagDescriptor(sig); |
|
1509 if (TagDescriptor == NULL){ |
|
1510 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); |
|
1511 return FALSE; |
|
1512 } |
|
1513 |
|
1514 |
|
1515 // Now we need to know which type to use. It depends on the version. |
|
1516 Version = cmsGetProfileVersion(hProfile); |
|
1517 if (TagDescriptor ->DecideType != NULL) { |
|
1518 |
|
1519 // Let the tag descriptor to decide the type base on depending on |
|
1520 // the data. This is useful for example on parametric curves, where |
|
1521 // curves specified by a table cannot be saved as parametric and needs |
|
1522 // to be revented to single v2-curves, even on v4 profiles. |
|
1523 |
|
1524 Type = TagDescriptor ->DecideType(Version, data); |
|
1525 } |
|
1526 else { |
|
1527 |
|
1528 Type = TagDescriptor ->SupportedTypes[0]; |
|
1529 } |
|
1530 |
|
1531 // Does the tag support this type? |
|
1532 if (!IsTypeSupported(TagDescriptor, Type)) { |
|
1533 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%x' for tag '%x'", Type, sig); |
|
1534 return FALSE; |
|
1535 } |
|
1536 |
|
1537 // Does we have a handler for this type? |
|
1538 TypeHandler = _cmsGetTagTypeHandler(Type); |
|
1539 if (TypeHandler == NULL) { |
|
1540 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%x' for tag '%x'", Type, sig); |
|
1541 return FALSE; // Should never happen |
|
1542 } |
|
1543 |
|
1544 // Fill fields on icc structure |
|
1545 Icc ->TagTypeHandlers[i] = TypeHandler; |
|
1546 Icc ->TagNames[i] = sig; |
|
1547 Icc ->TagSizes[i] = 0; |
|
1548 Icc ->TagOffsets[i] = 0; |
|
1549 Icc ->TagPtrs[i] = TypeHandler ->DupPtr(TypeHandler, data, TagDescriptor ->ElemCount); |
|
1550 |
|
1551 if (Icc ->TagPtrs[i] == NULL) { |
|
1552 |
|
1553 TypeHandler ->DupPtr(TypeHandler, data, TagDescriptor ->ElemCount); |
|
1554 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%x' for tag '%x'", Type, sig); |
|
1555 |
|
1556 return FALSE; |
|
1557 } |
|
1558 |
|
1559 return TRUE; |
|
1560 } |
|
1561 |
|
1562 // Read and write raw data. The only way those function would work and keep consistence with normal read and write |
|
1563 // is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained |
|
1564 // data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where |
|
1565 // raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows |
|
1566 // to write a tag as raw data and the read it as handled. |
|
1567 |
|
1568 cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) |
|
1569 { |
|
1570 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
|
1571 void *Object; |
|
1572 int i; |
|
1573 cmsIOHANDLER* MemIO; |
|
1574 cmsTagTypeHandler* TypeHandler = NULL; |
|
1575 cmsTagDescriptor* TagDescriptor = NULL; |
|
1576 cmsUInt32Number rc; |
|
1577 cmsUInt32Number Offset, TagSize; |
|
1578 |
|
1579 // Search for given tag in ICC profile directory |
|
1580 i = _cmsSearchTag(Icc, sig, TRUE); |
|
1581 if (i < 0) return 0; // Not found, return 0 |
|
1582 |
|
1583 // It is already read? |
|
1584 if (Icc -> TagPtrs[i] == NULL) { |
|
1585 |
|
1586 // No yet, get original position |
|
1587 Offset = Icc ->TagOffsets[i]; |
|
1588 TagSize = Icc ->TagSizes[i]; |
|
1589 |
|
1590 |
|
1591 // read the data directly, don't keep copy |
|
1592 if (data != NULL) { |
|
1593 |
|
1594 if (BufferSize < TagSize) |
|
1595 TagSize = BufferSize; |
|
1596 |
|
1597 if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) return 0; |
|
1598 if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) return 0; |
390 } |
1599 } |
391 } |
1600 |
392 |
1601 return Icc ->TagSizes[i]; |
393 |
1602 } |
394 Ptr = _cmsMalloc(size); |
1603 |
395 if (Ptr == NULL) return NULL; |
1604 // The data has been already read, or written. But wait!, maybe the user choosed to save as |
396 |
1605 // raw data. In this case, return the raw data directly |
397 CopyMemory(Ptr, Init, size); |
1606 if (Icc ->TagSaveAsRaw[i]) { |
398 |
1607 |
399 Icc ->TagNames[i] = sig; |
1608 if (data != NULL) { |
400 Icc ->TagSizes[i] = size; |
1609 |
401 Icc ->TagPtrs[i] = Ptr; |
1610 TagSize = Icc ->TagSizes[i]; |
402 |
1611 if (BufferSize < TagSize) |
403 return Ptr; |
1612 TagSize = BufferSize; |
404 } |
1613 |
405 |
1614 memmove(data, Icc ->TagPtrs[i], TagSize); |
406 |
1615 } |
407 |
1616 |
408 |
1617 return Icc ->TagSizes[i]; |
409 |
1618 } |
410 // Creates a profile from file read placeholder |
1619 |
411 |
1620 // Already readed, or previously set by cmsWriteTag(). We need to serialize that |
412 LPLCMSICCPROFILE _cmsCreateProfileFromFilePlaceholder(const char* FileName) |
1621 // data to raw in order to maintain consistency. |
413 { |
1622 Object = cmsReadTag(hProfile, sig); |
414 LPLCMSICCPROFILE NewIcc; |
1623 if (Object == NULL) return 0; |
415 LPVOID ICCfile = FileOpen(FileName); |
1624 |
416 |
1625 // Now we need to serialize to a memory block: just use a memory iohandler |
417 if (ICCfile == NULL) { |
1626 |
418 |
1627 if (data == NULL) { |
419 cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName); |
1628 MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); |
420 return NULL; |
1629 } else{ |
421 } |
1630 MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); |
422 |
1631 } |
423 NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder(); |
1632 if (MemIO == NULL) return 0; |
424 if (NewIcc == NULL) return NULL; |
1633 |
425 |
1634 // Obtain type handling for the tag |
426 strncpy(NewIcc -> PhysicalFile, FileName, MAX_PATH-1); |
1635 TypeHandler = Icc ->TagTypeHandlers[i]; |
427 NewIcc -> PhysicalFile[MAX_PATH-1] = 0; |
1636 TagDescriptor = _cmsGetTagDescriptor(sig); |
428 |
1637 |
429 NewIcc ->stream = ICCfile; |
1638 // Serialize |
430 |
1639 if (!TypeHandler ->WritePtr(TypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) return 0; |
431 NewIcc ->Read = FileRead; |
1640 |
432 NewIcc ->Seek = FileSeek; |
1641 // Get Size and close |
433 NewIcc ->Tell = FileTell; |
1642 rc = MemIO ->Tell(MemIO); |
434 NewIcc ->Close = FileClose; |
1643 cmsCloseIOhandler(MemIO); // Ignore return code this time |
435 NewIcc ->Grow = FileGrow; |
1644 |
436 NewIcc ->Write = NULL; |
1645 return rc; |
437 |
1646 } |
438 NewIcc ->IsWrite = FALSE; |
1647 |
439 |
1648 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without |
440 |
1649 // checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading |
441 |
1650 // it as cooked without serializing does result into an error. If that is wha you want, you will need to dump |
442 |
1651 // the profile to memry or disk and then reopen it. |
443 return NewIcc; |
1652 cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) |
444 } |
1653 { |
445 |
1654 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
446 |
1655 int i; |
447 // Creates a profile from memory read placeholder |
1656 |
448 |
1657 if (!_cmsNewTag(Icc, sig, &i)) return FALSE; |
449 LPLCMSICCPROFILE _cmsCreateProfileFromMemPlaceholder(LPVOID MemPtr, DWORD dwSize) |
1658 |
450 { |
1659 // Mark the tag as being written as RAW |
451 |
1660 Icc ->TagSaveAsRaw[i] = TRUE; |
452 LPLCMSICCPROFILE NewIcc; |
1661 Icc ->TagNames[i] = sig; |
453 LPVOID ICCfile = MemoryOpen((LPBYTE) MemPtr, (size_t) dwSize, 'r'); |
1662 Icc ->TagLinked[i] = (cmsTagSignature) 0; |
454 |
1663 |
455 |
1664 // Keep a copy of the block |
456 if (ICCfile == NULL) { |
1665 Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); |
457 |
1666 Icc ->TagSizes[i] = Size; |
458 cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't allocate %ld bytes for profile", dwSize); |
1667 |
459 return NULL; |
|
460 } |
|
461 |
|
462 |
|
463 NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder(); |
|
464 if (NewIcc == NULL) return NULL; |
|
465 |
|
466 NewIcc -> PhysicalFile[0] = 0; |
|
467 NewIcc ->stream = ICCfile; |
|
468 |
|
469 NewIcc ->Read = MemoryRead; |
|
470 NewIcc ->Seek = MemorySeek; |
|
471 NewIcc ->Tell = MemoryTell; |
|
472 NewIcc ->Close = MemoryClose; |
|
473 NewIcc ->Grow = MemoryGrow; |
|
474 NewIcc ->Write = MemoryWrite; |
|
475 |
|
476 NewIcc ->IsWrite = FALSE; |
|
477 |
|
478 |
|
479 return NewIcc; |
|
480 } |
|
481 |
|
482 |
|
483 // Turn a placeholder into file writter |
|
484 |
|
485 void _cmsSetSaveToDisk(LPLCMSICCPROFILE Icc, const char* FileName) |
|
486 { |
|
487 |
|
488 if (FileName == NULL) { |
|
489 |
|
490 Icc ->stream = NULL; |
|
491 } |
|
492 else { |
|
493 |
|
494 Icc ->stream = fopen(FileName, "wb"); |
|
495 if (Icc ->stream == NULL) |
|
496 cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to file '%s'", FileName); |
|
497 } |
|
498 |
|
499 Icc ->Write = FileWrite; // Save to disk |
|
500 Icc ->Close = FileClose; |
|
501 } |
|
502 |
|
503 |
|
504 |
|
505 // Turn a placeholder into memory writter |
|
506 |
|
507 void _cmsSetSaveToMemory(LPLCMSICCPROFILE Icc, LPVOID MemPtr, size_t dwSize) |
|
508 { |
|
509 |
|
510 if (MemPtr == NULL) { |
|
511 |
|
512 Icc ->stream = NULL; |
|
513 } |
|
514 else { |
|
515 |
|
516 Icc ->stream = (FILEMEM*) MemoryOpen((LPBYTE) MemPtr, dwSize, 'w'); |
|
517 if (Icc ->stream == NULL) |
|
518 cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to memory"); |
|
519 } |
|
520 |
|
521 Icc ->Write = MemoryWrite; |
|
522 Icc ->Close = MemoryClose; |
|
523 } |
|
524 |
|
525 |
|
526 // ----------------------------------------------------------------------- Set/Get several struct members |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 LCMSBOOL LCMSEXPORT cmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile) |
|
532 { |
|
533 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
534 *Dest = Icc -> MediaWhitePoint; |
|
535 return TRUE; |
|
536 } |
|
537 |
|
538 |
|
539 LCMSBOOL LCMSEXPORT cmsTakeMediaBlackPoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile) |
|
540 { |
|
541 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
542 *Dest = Icc -> MediaBlackPoint; |
|
543 return TRUE; |
|
544 } |
|
545 |
|
546 LCMSBOOL LCMSEXPORT cmsTakeIluminant(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile) |
|
547 { |
|
548 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
549 *Dest = Icc -> Illuminant; |
|
550 return TRUE; |
|
551 } |
|
552 |
|
553 int LCMSEXPORT cmsTakeRenderingIntent(cmsHPROFILE hProfile) |
|
554 { |
|
555 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
556 return (int) Icc -> RenderingIntent; |
|
557 } |
|
558 |
|
559 void LCMSEXPORT cmsSetRenderingIntent(cmsHPROFILE hProfile, int RenderingIntent) |
|
560 { |
|
561 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
562 Icc -> RenderingIntent = (icRenderingIntent) RenderingIntent; |
|
563 } |
|
564 |
|
565 |
|
566 DWORD LCMSEXPORT cmsTakeHeaderFlags(cmsHPROFILE hProfile) |
|
567 { |
|
568 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
569 return (DWORD) Icc -> flags; |
|
570 } |
|
571 |
|
572 void LCMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, DWORD Flags) |
|
573 { |
|
574 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
575 Icc -> flags = (icUInt32Number) Flags; |
|
576 } |
|
577 |
|
578 DWORD LCMSEXPORT cmsTakeHeaderAttributes(cmsHPROFILE hProfile) |
|
579 { |
|
580 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
581 return (DWORD) Icc -> attributes; |
|
582 } |
|
583 |
|
584 void LCMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, DWORD Flags) |
|
585 { |
|
586 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
587 Icc -> attributes = (icUInt32Number) Flags; |
|
588 } |
|
589 |
|
590 |
|
591 const BYTE* LCMSEXPORT cmsTakeProfileID(cmsHPROFILE hProfile) |
|
592 { |
|
593 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
594 return Icc ->ProfileID; |
|
595 } |
|
596 |
|
597 void LCMSEXPORT cmsSetProfileID(cmsHPROFILE hProfile, LPBYTE ProfileID) |
|
598 { |
|
599 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
600 CopyMemory(Icc -> ProfileID, ProfileID, 16); |
|
601 } |
|
602 |
|
603 |
|
604 LCMSBOOL LCMSEXPORT cmsTakeCreationDateTime(struct tm *Dest, cmsHPROFILE hProfile) |
|
605 { |
|
606 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
607 CopyMemory(Dest, &Icc ->Created, sizeof(struct tm)); |
|
608 return TRUE; |
1668 return TRUE; |
609 } |
1669 } |
610 |
1670 |
611 |
1671 // Using this function you can collapse several tag entries to the same block in the profile |
612 icColorSpaceSignature LCMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) |
1672 cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) |
613 { |
1673 { |
614 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
1674 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; |
615 return Icc -> PCS; |
1675 int i; |
616 } |
1676 |
617 |
1677 if (!_cmsNewTag(Icc, sig, &i)) return FALSE; |
618 |
1678 |
619 void LCMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, icColorSpaceSignature pcs) |
1679 // Keep necessary information |
620 { |
1680 Icc ->TagSaveAsRaw[i] = FALSE; |
621 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
1681 Icc ->TagNames[i] = sig; |
622 Icc -> PCS = pcs; |
1682 Icc ->TagLinked[i] = dest; |
623 } |
1683 |
624 |
1684 Icc ->TagPtrs[i] = NULL; |
625 icColorSpaceSignature LCMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) |
1685 Icc ->TagSizes[i] = 0; |
626 { |
1686 Icc ->TagOffsets[i] = 0; |
627 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
1687 |
628 return Icc -> ColorSpace; |
|
629 } |
|
630 |
|
631 void LCMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, icColorSpaceSignature sig) |
|
632 { |
|
633 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
634 Icc -> ColorSpace = sig; |
|
635 } |
|
636 |
|
637 icProfileClassSignature LCMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) |
|
638 { |
|
639 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
640 return Icc -> DeviceClass; |
|
641 } |
|
642 |
|
643 DWORD LCMSEXPORT cmsGetProfileICCversion(cmsHPROFILE hProfile) |
|
644 { |
|
645 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
646 return (DWORD) Icc -> Version; |
|
647 } |
|
648 |
|
649 void LCMSEXPORT cmsSetProfileICCversion(cmsHPROFILE hProfile, DWORD Version) |
|
650 { |
|
651 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
652 Icc -> Version = Version; |
|
653 } |
|
654 |
|
655 |
|
656 void LCMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, icProfileClassSignature sig) |
|
657 { |
|
658 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; |
|
659 Icc -> DeviceClass = sig; |
|
660 } |
|
661 |
|
662 |
|
663 // -------------------------------------------------------------------------------------------------------------- |
|
664 |
|
665 |
|
666 static |
|
667 int SizeOfGammaTab(LPGAMMATABLE In) |
|
668 { |
|
669 return sizeof(GAMMATABLE) + (In -> nEntries - 1)*sizeof(WORD); |
|
670 } |
|
671 |
|
672 |
|
673 // Creates a phantom tag holding a memory block |
|
674 |
|
675 static |
|
676 LPVOID DupBlock(LPLCMSICCPROFILE Icc, LPVOID Block, size_t size) |
|
677 { |
|
678 if (Block != NULL && size > 0) |
|
679 return _cmsInitTag(Icc, (icTagSignature) 0, size, Block); |
|
680 else |
|
681 return NULL; |
|
682 |
|
683 } |
|
684 |
|
685 // This is tricky, since LUT structs does have pointers |
|
686 |
|
687 LCMSBOOL LCMSEXPORT _cmsAddLUTTag(cmsHPROFILE hProfile, icTagSignature sig, const void* lut) |
|
688 { |
|
689 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
690 LPLUT Orig, Stored; |
|
691 unsigned int i; |
|
692 |
|
693 // The struct itself |
|
694 |
|
695 Orig = (LPLUT) lut; |
|
696 Stored = (LPLUT) _cmsInitTag(Icc, (icTagSignature) sig, sizeof(LUT), lut); |
|
697 |
|
698 // dup' the memory blocks |
|
699 for (i=0; i < Orig ->InputChan; i++) |
|
700 Stored -> L1[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L1[i], |
|
701 sizeof(WORD) * Orig ->In16params.nSamples); |
|
702 |
|
703 for (i=0; i < Orig ->OutputChan; i++) |
|
704 Stored -> L2[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L2[i], |
|
705 sizeof(WORD) * Orig ->Out16params.nSamples); |
|
706 |
|
707 Stored -> T = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->T, Orig -> Tsize); |
|
708 |
|
709 // Zero any additional pointer |
|
710 Stored ->CLut16params.p8 = NULL; |
|
711 return TRUE; |
|
712 } |
|
713 |
|
714 |
|
715 LCMSBOOL LCMSEXPORT _cmsAddXYZTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* XYZ) |
|
716 { |
|
717 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
718 |
|
719 _cmsInitTag(Icc, sig, sizeof(cmsCIEXYZ), XYZ); |
|
720 return TRUE; |
|
721 } |
|
722 |
|
723 |
|
724 LCMSBOOL LCMSEXPORT _cmsAddTextTag(cmsHPROFILE hProfile, icTagSignature sig, const char* Text) |
|
725 { |
|
726 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
727 |
|
728 _cmsInitTag(Icc, sig, strlen(Text)+1, (LPVOID) Text); |
|
729 return TRUE; |
|
730 } |
|
731 |
|
732 LCMSBOOL LCMSEXPORT _cmsAddGammaTag(cmsHPROFILE hProfile, icTagSignature sig, LPGAMMATABLE TransferFunction) |
|
733 { |
|
734 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
735 |
|
736 _cmsInitTag(Icc, sig, SizeOfGammaTab(TransferFunction), TransferFunction); |
|
737 return TRUE; |
1688 return TRUE; |
738 } |
1689 } |
739 |
|
740 |
|
741 LCMSBOOL LCMSEXPORT _cmsAddChromaticityTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIExyYTRIPLE Chrm) |
|
742 { |
|
743 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
744 |
|
745 _cmsInitTag(Icc, sig, sizeof(cmsCIExyYTRIPLE), Chrm); |
|
746 return TRUE; |
|
747 } |
|
748 |
|
749 |
|
750 LCMSBOOL LCMSEXPORT _cmsAddSequenceDescriptionTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsSEQ pseq) |
|
751 { |
|
752 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
753 |
|
754 _cmsInitTag(Icc, sig, sizeof(int) + pseq -> n * sizeof(cmsPSEQDESC), pseq); |
|
755 return TRUE; |
|
756 |
|
757 } |
|
758 |
|
759 |
|
760 LCMSBOOL LCMSEXPORT _cmsAddNamedColorTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc) |
|
761 { |
|
762 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
763 |
|
764 _cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc); |
|
765 return TRUE; |
|
766 } |
|
767 |
|
768 |
|
769 LCMSBOOL LCMSEXPORT _cmsAddDateTimeTag(cmsHPROFILE hProfile, icTagSignature sig, struct tm *DateTime) |
|
770 { |
|
771 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
772 |
|
773 _cmsInitTag(Icc, sig, sizeof(struct tm), DateTime); |
|
774 return TRUE; |
|
775 } |
|
776 |
|
777 |
|
778 LCMSBOOL LCMSEXPORT _cmsAddColorantTableTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc) |
|
779 { |
|
780 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
781 |
|
782 _cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc); |
|
783 return TRUE; |
|
784 } |
|
785 |
|
786 |
|
787 LCMSBOOL LCMSEXPORT _cmsAddChromaticAdaptationTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* mat) |
|
788 { |
|
789 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; |
|
790 |
|
791 _cmsInitTag(Icc, sig, 3*sizeof(cmsCIEXYZ), mat); |
|
792 return TRUE; |
|
793 |
|
794 } |
|
795 |
|
796 |
|