1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                                                                             %
7%                                 N   N  TTTTT                                %
8%                                 NN  N    T                                  %
9%                                 N N N    T                                  %
10%                                 N  NN    T                                  %
11%                                 N   N    T                                  %
12%                                                                             %
13%                                                                             %
14%                   Windows NT Feature Methods for MagickCore                 %
15%                                                                             %
16%                               Software Design                               %
17%                                    Cristy                                   %
18%                                December 1996                                %
19%                                                                             %
20%                                                                             %
21%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
22%  dedicated to making software imaging solutions freely available.           %
23%                                                                             %
24%  You may not use this file except in compliance with the License.  You may  %
25%  obtain a copy of the License at                                            %
26%                                                                             %
27%    http://www.imagemagick.org/script/license.php                            %
28%                                                                             %
29%  Unless required by applicable law or agreed to in writing, software        %
30%  distributed under the License is distributed on an "AS IS" BASIS,          %
31%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32%  See the License for the specific language governing permissions and        %
33%  limitations under the License.                                             %
34%                                                                             %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "MagickCore/studio.h"
44#if defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
45#define WIN32_LEAN_AND_MEAN
46#define VC_EXTRALEAN
47#include <windows.h>
48#include "MagickCore/cache.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/colorspace-private.h"
51#include "MagickCore/draw.h"
52#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/image-private.h"
55#include "MagickCore/memory_.h"
56#include "MagickCore/monitor.h"
57#include "MagickCore/monitor-private.h"
58#include "MagickCore/nt-base.h"
59#include "MagickCore/nt-base-private.h"
60#include "MagickCore/pixel-accessor.h"
61#include "MagickCore/quantum.h"
62#include "MagickCore/string_.h"
63#include "MagickCore/token.h"
64#include "MagickCore/splay-tree.h"
65#include "MagickCore/utility.h"
66
67/*
68%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69%                                                                             %
70%                                                                             %
71%                                                                             %
72%   C r o p I m a g e T o H B i t m a p                                       %
73%                                                                             %
74%                                                                             %
75%                                                                             %
76%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77%
78%  CropImageToHBITMAP() extracts a specified region of the image and returns
79%  it as a Windows HBITMAP. While the same functionality can be accomplished by
80%  invoking CropImage() followed by ImageToHBITMAP(), this method is more
81%  efficient since it copies pixels directly to the HBITMAP.
82%
83%  The format of the CropImageToHBITMAP method is:
84%
85%      HBITMAP CropImageToHBITMAP(Image* image,const RectangleInfo *geometry,
86%        ExceptionInfo *exception)
87%
88%  A description of each parameter follows:
89%
90%    o image: the image.
91%
92%    o geometry: Define the region of the image to crop with members
93%      x, y, width, and height.
94%
95%    o exception: return any errors or warnings in this structure.
96%
97*/
98MagickExport void *CropImageToHBITMAP(Image *image,
99  const RectangleInfo *geometry,ExceptionInfo *exception)
100{
101#define CropImageTag  "Crop/Image"
102
103  BITMAP
104    bitmap;
105
106  HBITMAP
107    bitmapH;
108
109  HANDLE
110    bitmap_bitsH;
111
112  MagickBooleanType
113    proceed;
114
115  RectangleInfo
116    page;
117
118  register const Quantum
119    *p;
120
121  register RGBQUAD
122    *q;
123
124  RGBQUAD
125    *bitmap_bits;
126
127  ssize_t
128    y;
129
130  /*
131    Check crop geometry.
132  */
133  assert(image != (const Image *) NULL);
134  assert(image->signature == MagickCoreSignature);
135  if (image->debug != MagickFalse)
136    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
137  assert(geometry != (const RectangleInfo *) NULL);
138  assert(exception != (ExceptionInfo *) NULL);
139  assert(exception->signature == MagickCoreSignature);
140  if (((geometry->x+(ssize_t) geometry->width) < 0) ||
141      ((geometry->y+(ssize_t) geometry->height) < 0) ||
142      (geometry->x >= (ssize_t) image->columns) ||
143      (geometry->y >= (ssize_t) image->rows))
144    ThrowImageException(OptionError,"GeometryDoesNotContainImage");
145  page=(*geometry);
146  if ((page.x+(ssize_t) page.width) > (ssize_t) image->columns)
147    page.width=image->columns-page.x;
148  if ((page.y+(ssize_t) page.height) > (ssize_t) image->rows)
149    page.height=image->rows-page.y;
150  if (page.x < 0)
151    {
152      page.width+=page.x;
153      page.x=0;
154    }
155  if (page.y < 0)
156    {
157      page.height+=page.y;
158      page.y=0;
159    }
160
161  if ((page.width == 0) || (page.height == 0))
162    ThrowImageException(OptionError,"GeometryDimensionsAreZero");
163  /*
164    Initialize crop image attributes.
165  */
166  bitmap.bmType         = 0;
167  bitmap.bmWidth        = (LONG) page.width;
168  bitmap.bmHeight       = (LONG) page.height;
169  bitmap.bmWidthBytes   = bitmap.bmWidth * 4;
170  bitmap.bmPlanes       = 1;
171  bitmap.bmBitsPixel    = 32;
172  bitmap.bmBits         = NULL;
173
174  bitmap_bitsH=(HANDLE) GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,page.width*
175    page.height*bitmap.bmBitsPixel);
176  if (bitmap_bitsH == NULL)
177    return(NULL);
178  bitmap_bits=(RGBQUAD *) GlobalLock((HGLOBAL) bitmap_bitsH);
179  if ( bitmap.bmBits == NULL )
180    bitmap.bmBits = bitmap_bits;
181  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
182    SetImageColorspace(image,sRGBColorspace,exception);
183  /*
184    Extract crop image.
185  */
186  q=bitmap_bits;
187  for (y=0; y < (ssize_t) page.height; y++)
188  {
189    register ssize_t
190      x;
191
192    p=GetVirtualPixels(image,page.x,page.y+y,page.width,1,exception);
193    if (p == (const Quantum *) NULL)
194      break;
195
196    /* Transfer pixels, scaling to Quantum */
197    for( x=(ssize_t) page.width ; x> 0 ; x-- )
198    {
199      q->rgbRed = ScaleQuantumToChar(GetPixelRed(image,p));
200      q->rgbGreen = ScaleQuantumToChar(GetPixelGreen(image,p));
201      q->rgbBlue = ScaleQuantumToChar(GetPixelBlue(image,p));
202      q->rgbReserved = 0;
203      p+=GetPixelChannels(image);
204      q++;
205    }
206    proceed=SetImageProgress(image,CropImageTag,y,page.height);
207    if (proceed == MagickFalse)
208      break;
209  }
210  if (y < (ssize_t) page.height)
211    {
212      GlobalUnlock((HGLOBAL) bitmap_bitsH);
213      GlobalFree((HGLOBAL) bitmap_bitsH);
214      return((void *) NULL);
215    }
216  bitmap.bmBits=bitmap_bits;
217  bitmapH=CreateBitmapIndirect(&bitmap);
218  GlobalUnlock((HGLOBAL) bitmap_bitsH);
219  GlobalFree((HGLOBAL) bitmap_bitsH);
220  return((void *) bitmapH);
221}
222
223/*
224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
225%                                                                             %
226%                                                                             %
227%                                                                             %
228%   I s M a g i c k C o n f l i c t                                           %
229%                                                                             %
230%                                                                             %
231%                                                                             %
232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233%
234%  IsMagickConflict() returns true if the image format conflicts with a logical
235%  drive (.e.g. X:).
236%
237%  The format of the IsMagickConflict method is:
238%
239%      MagickBooleanType IsMagickConflict(const char *magick)
240%
241%  A description of each parameter follows:
242%
243%    o magick: Specifies the image format.
244%
245*/
246MagickExport MagickBooleanType NTIsMagickConflict(const char *magick)
247{
248  MagickBooleanType
249    status;
250
251  assert(magick != (char *) NULL);
252  if (strlen(magick) > 1)
253    return(MagickFalse);
254  status=(GetLogicalDrives() & (1 << ((toupper((int) (*magick)))-'A'))) != 0 ?
255    MagickTrue : MagickFalse;
256  return(status);
257}
258
259/*
260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261%                                                                             %
262%                                                                             %
263%                                                                             %
264%   N T A c q u i r e T y p e C a c h e                                       %
265%                                                                             %
266%                                                                             %
267%                                                                             %
268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269%
270%  NTAcquireTypeCache() loads a Windows TrueType fonts.
271%
272%  The format of the NTAcquireTypeCache method is:
273%
274%      MagickBooleanType NTAcquireTypeCache(SplayTreeInfo *type_cache)
275%
276%  A description of each parameter follows:
277%
278%    o type_cache: A linked list of fonts.
279%
280*/
281MagickExport MagickBooleanType NTAcquireTypeCache(SplayTreeInfo *type_cache,
282  ExceptionInfo *exception)
283{
284  HKEY
285    reg_key = (HKEY) INVALID_HANDLE_VALUE;
286
287  LONG
288    res;
289
290  int
291    list_entries = 0;
292
293  char
294    buffer[MagickPathExtent],
295    system_root[MagickPathExtent],
296    font_root[MagickPathExtent];
297
298  DWORD
299    type,
300    system_root_length;
301
302  MagickBooleanType
303    status;
304
305  /*
306    Try to find the right Windows*\CurrentVersion key, the SystemRoot and
307    then the Fonts key
308  */
309  res = RegOpenKeyExA (HKEY_LOCAL_MACHINE,
310    "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &reg_key);
311  if (res == ERROR_SUCCESS) {
312    system_root_length=sizeof(system_root)-1;
313    res = RegQueryValueExA(reg_key,"SystemRoot",NULL, &type,
314      (BYTE*) system_root, &system_root_length);
315  }
316  if (res != ERROR_SUCCESS) {
317    res = RegOpenKeyExA (HKEY_LOCAL_MACHINE,
318      "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &reg_key);
319    if (res == ERROR_SUCCESS) {
320      system_root_length=sizeof(system_root)-1;
321      res = RegQueryValueExA(reg_key,"SystemRoot",NULL, &type,
322        (BYTE*)system_root, &system_root_length);
323    }
324  }
325  if (res == ERROR_SUCCESS)
326    res = RegOpenKeyExA (reg_key, "Fonts",0, KEY_READ, &reg_key);
327  if (res != ERROR_SUCCESS)
328    return(MagickFalse);
329  *font_root='\0';
330  (void) CopyMagickString(buffer,system_root,MagickPathExtent);
331  (void) ConcatenateMagickString(buffer,"\\fonts\\arial.ttf",MagickPathExtent);
332  if (IsPathAccessible(buffer) != MagickFalse)
333    {
334      (void) CopyMagickString(font_root,system_root,MagickPathExtent);
335      (void) ConcatenateMagickString(font_root,"\\fonts\\",MagickPathExtent);
336    }
337  else
338    {
339      (void) CopyMagickString(font_root,system_root,MagickPathExtent);
340      (void) ConcatenateMagickString(font_root,"\\",MagickPathExtent);
341    }
342
343  {
344    TypeInfo
345      *type_info;
346
347    DWORD
348      registry_index = 0,
349      type,
350      value_data_size,
351      value_name_length;
352
353    char
354      value_data[MagickPathExtent],
355      value_name[MagickPathExtent];
356
357    res = ERROR_SUCCESS;
358
359    while (res != ERROR_NO_MORE_ITEMS)
360      {
361        char
362          *family_extent,
363          token[MagickPathExtent],
364          *pos,
365          *q;
366
367        value_name_length = sizeof(value_name) - 1;
368        value_data_size = sizeof(value_data) - 1;
369        res = RegEnumValueA ( reg_key, registry_index, value_name,
370          &value_name_length, 0, &type, (BYTE*)value_data, &value_data_size);
371        registry_index++;
372        if (res != ERROR_SUCCESS)
373          continue;
374        if ( (pos = strstr(value_name, " (TrueType)")) == (char*) NULL )
375          continue;
376        *pos='\0'; /* Remove (TrueType) from string */
377
378        type_info=(TypeInfo *) AcquireMagickMemory(sizeof(*type_info));
379        if (type_info == (TypeInfo *) NULL)
380          ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
381        (void) ResetMagickMemory(type_info,0,sizeof(TypeInfo));
382
383        type_info->path=ConstantString("Windows Fonts");
384        type_info->signature=MagickCoreSignature;
385
386        /* Name */
387        (void) CopyMagickString(buffer,value_name,MagickPathExtent);
388        for(pos = buffer; *pos != 0 ; pos++)
389          if (*pos == ' ')
390            *pos = '-';
391        type_info->name=ConstantString(buffer);
392
393        /* Fullname */
394        type_info->description=ConstantString(value_name);
395
396        /* Format */
397        type_info->format=ConstantString("truetype");
398
399        /* Glyphs */
400        if (strchr(value_data,'\\') != (char *) NULL)
401          (void) CopyMagickString(buffer,value_data,MagickPathExtent);
402        else
403          {
404            (void) CopyMagickString(buffer,font_root,MagickPathExtent);
405            (void) ConcatenateMagickString(buffer,value_data,MagickPathExtent);
406          }
407
408        LocaleLower(buffer);
409        type_info->glyphs=ConstantString(buffer);
410
411        type_info->stretch=NormalStretch;
412        type_info->style=NormalStyle;
413        type_info->weight=400;
414
415        /* Some fonts are known to require special encodings */
416        if ( (LocaleCompare(type_info->name, "Symbol") == 0 ) ||
417             (LocaleCompare(type_info->name, "Wingdings") == 0 ) ||
418             (LocaleCompare(type_info->name, "Wingdings-2") == 0 ) ||
419             (LocaleCompare(type_info->name, "Wingdings-3") == 0 ) )
420          type_info->encoding=ConstantString("AppleRoman");
421
422        family_extent=value_name;
423
424        for (q=value_name; *q != '\0'; )
425          {
426            GetNextToken(q,(const char **) &q,MagickPathExtent,token);
427            if (*token == '\0')
428              break;
429
430            if (LocaleCompare(token,"Italic") == 0)
431              {
432                type_info->style=ItalicStyle;
433              }
434
435            else if (LocaleCompare(token,"Oblique") == 0)
436              {
437                type_info->style=ObliqueStyle;
438              }
439
440            else if (LocaleCompare(token,"Bold") == 0)
441              {
442                type_info->weight=700;
443              }
444
445            else if (LocaleCompare(token,"Thin") == 0)
446              {
447                type_info->weight=100;
448              }
449
450            else if ( (LocaleCompare(token,"ExtraLight") == 0) ||
451                      (LocaleCompare(token,"UltraLight") == 0) )
452              {
453                type_info->weight=200;
454              }
455
456            else if (LocaleCompare(token,"Light") == 0)
457              {
458                type_info->weight=300;
459              }
460
461            else if ( (LocaleCompare(token,"Normal") == 0) ||
462                      (LocaleCompare(token,"Regular") == 0) )
463              {
464                type_info->weight=400;
465              }
466
467            else if (LocaleCompare(token,"Medium") == 0)
468              {
469                type_info->weight=500;
470              }
471
472            else if ( (LocaleCompare(token,"SemiBold") == 0) ||
473                      (LocaleCompare(token,"DemiBold") == 0) )
474              {
475                type_info->weight=600;
476              }
477
478            else if ( (LocaleCompare(token,"ExtraBold") == 0) ||
479                      (LocaleCompare(token,"UltraBold") == 0) )
480              {
481                type_info->weight=800;
482              }
483
484            else if ( (LocaleCompare(token,"Heavy") == 0) ||
485                      (LocaleCompare(token,"Black") == 0) )
486              {
487                type_info->weight=900;
488              }
489
490            else if (LocaleCompare(token,"Condensed") == 0)
491              {
492                type_info->stretch = CondensedStretch;
493              }
494
495            else if (LocaleCompare(token,"Expanded") == 0)
496              {
497                type_info->stretch = ExpandedStretch;
498              }
499
500            else if (LocaleCompare(token,"ExtraCondensed") == 0)
501              {
502                type_info->stretch = ExtraCondensedStretch;
503              }
504
505            else if (LocaleCompare(token,"ExtraExpanded") == 0)
506              {
507                type_info->stretch = ExtraExpandedStretch;
508              }
509
510            else if (LocaleCompare(token,"SemiCondensed") == 0)
511              {
512                type_info->stretch = SemiCondensedStretch;
513              }
514
515            else if (LocaleCompare(token,"SemiExpanded") == 0)
516              {
517                type_info->stretch = SemiExpandedStretch;
518              }
519
520            else if (LocaleCompare(token,"UltraCondensed") == 0)
521              {
522                type_info->stretch = UltraCondensedStretch;
523              }
524
525            else if (LocaleCompare(token,"UltraExpanded") == 0)
526              {
527                type_info->stretch = UltraExpandedStretch;
528              }
529
530            else
531              {
532                family_extent=q;
533              }
534          }
535
536        (void) CopyMagickString(buffer,value_name,family_extent-value_name+1);
537        StripString(buffer);
538        type_info->family=ConstantString(buffer);
539
540        list_entries++;
541        status=AddValueToSplayTree(type_cache,type_info->name,type_info);
542        if (status == MagickFalse)
543          (void) ThrowMagickException(exception,GetMagickModule(),
544            ResourceLimitError,"MemoryAllocationFailed","`%s'",type_info->name);
545      }
546  }
547  RegCloseKey ( reg_key );
548  return(MagickTrue);
549}
550
551/*
552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553%                                                                             %
554%                                                                             %
555%                                                                             %
556%   I m a g e T o H B i t m a p                                               %
557%                                                                             %
558%                                                                             %
559%                                                                             %
560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561%
562%  ImageToHBITMAP() creates a Windows HBITMAP from an image.
563%
564%  The format of the ImageToHBITMAP method is:
565%
566%      HBITMAP ImageToHBITMAP(Image *image,Exceptioninfo *exception)
567%
568%  A description of each parameter follows:
569%
570%    o image: the image to convert.
571%
572*/
573MagickExport void *ImageToHBITMAP(Image *image,ExceptionInfo *exception)
574{
575  BITMAP
576    bitmap;
577
578  HANDLE
579    bitmap_bitsH;
580
581  HBITMAP
582    bitmapH;
583
584  register ssize_t
585    x;
586
587  register const Quantum
588    *p;
589
590  register RGBQUAD
591    *q;
592
593  RGBQUAD
594    *bitmap_bits;
595
596  size_t
597    length;
598
599  ssize_t
600    y;
601
602  (void) ResetMagickMemory(&bitmap,0,sizeof(bitmap));
603  bitmap.bmType=0;
604  bitmap.bmWidth=(LONG) image->columns;
605  bitmap.bmHeight=(LONG) image->rows;
606  bitmap.bmWidthBytes=4*bitmap.bmWidth;
607  bitmap.bmPlanes=1;
608  bitmap.bmBitsPixel=32;
609  bitmap.bmBits=NULL;
610  length=bitmap.bmWidthBytes*bitmap.bmHeight;
611  bitmap_bitsH=(HANDLE) GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,length);
612  if (bitmap_bitsH == NULL)
613    {
614      char
615        *message;
616
617      message=GetExceptionMessage(errno);
618      (void) ThrowMagickException(exception,GetMagickModule(),
619        ResourceLimitError,"MemoryAllocationFailed","`%s'",message);
620      message=DestroyString(message);
621      return(NULL);
622    }
623  bitmap_bits=(RGBQUAD *) GlobalLock((HGLOBAL) bitmap_bitsH);
624  q=bitmap_bits;
625  if (bitmap.bmBits == NULL)
626    bitmap.bmBits=bitmap_bits;
627  (void) SetImageColorspace(image,sRGBColorspace,exception);
628  for (y=0; y < (ssize_t) image->rows; y++)
629  {
630    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
631    if (p == (const Quantum *) NULL)
632      break;
633    for (x=0; x < (ssize_t) image->columns; x++)
634    {
635      q->rgbRed=ScaleQuantumToChar(GetPixelRed(image,p));
636      q->rgbGreen=ScaleQuantumToChar(GetPixelGreen(image,p));
637      q->rgbBlue=ScaleQuantumToChar(GetPixelBlue(image,p));
638      q->rgbReserved=0;
639      p+=GetPixelChannels(image);
640      q++;
641    }
642  }
643  bitmap.bmBits=bitmap_bits;
644  bitmapH=CreateBitmapIndirect(&bitmap);
645  if (bitmapH == NULL)
646    {
647      char
648        *message;
649
650      message=GetExceptionMessage(errno);
651      (void) ThrowMagickException(exception,GetMagickModule(),
652        ResourceLimitError,"MemoryAllocationFailed","`%s'",message);
653      message=DestroyString(message);
654    }
655  GlobalUnlock((HGLOBAL) bitmap_bitsH);
656  GlobalFree((HGLOBAL) bitmap_bitsH);
657  return((void *) bitmapH);
658}
659
660#endif
661